观察者模式

观察者模式

观察者模式(Observer)通常又被称为发布-订阅者模式消息机制,它定义了对象间的一对多的依赖关系,只要当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,解决了主体对象与观察者之间功能的耦合,即一个对象状态改变给其他对象通知的问题。

我们平时对DOM的事件绑定就是一个非常典型的发布-订阅者模式,这里我们需要监听用户点击按钮这个动作,但是我们却无法知道用户什么时候去点击,所以我们订阅按钮上的click事件,只要按钮被点击时,那么按钮就会向订阅者发布这个消息,我们就可以做对应的操作了。

创建一个观察者

首先我们需要创建一个观察者对象,它包含一个消息容器和三个方法,分别是订阅消息方法on,取消订阅消息方法off,发送订阅消息subscribe

const const Observe = (function () {
    // 防止消息队列暴露而被篡改,将消息容器设置为私有变量
    let __message = {};
    return {
        // 注册消息接口
        on : function() {},
        // 发布消息接口
        subscribe : function() {},
        // 移除消息接口
        off : function() {}
    }
})();

注册消息方法

注册消息方法的作用是将订阅者注册的消息推入到消息队列中,因此需要传递两个参数:消息类型和对应的处理函数。要注意的是,如果此消息不存在,则要创建一个该消息类型并将该消息推入消息队列中,如果此消息已经存在则将对应的方法推入到执行方法队列中。

// 注册消息接口
on: function(type, fn) {
    if (typeof __message[type] === 'undefined') {
        // 如果此消息不存在,创建一个该消息类型
        __message[type] = [fn];
    } else {
        // 如果此消息存在,直接将执行方法推入该消息对应的执行队列中
        __message[type].push(fn);
    }
}

发布消息方法

发布消息,其功能就是当观察者发布一个消息是将所有订阅者订阅的消息依次执行,也需要传两个参数,分别是消息类型和对应执行函数时所需要的参数,其中消息类型是必须的。

// 发布消息接口
subscribe: function(type, args) {
    // 如果该消息没有注册,直接返回
    if (!__message[type]) return;
    // 定义消息信息
    let events = {
        type: type,  // 消息类型
        args: args || {}  // 参数
    };
    let	i = 0,
        len = __message[type].length;
    for (; i < len; ++i) {
        // 依次执行注册消息对应的方法
        __message[type][i].call(this, events);
    }
}

移除消息方法

移除消息方法,其功能就是将订阅者注销的信息从消息队列移除,也需要传递消息类型和执行队列中的某一函数两个参数。这里为了避免删除消息不存在的情况,所以要对其消息存在性进行校验。

// 移除消息接口
off: function(type, fn) {
    // 如果消息队列存在
    if (__message[type] instanceof Array) {
        let i = __message.length - 1;
        for (; i >= 0; --i) {
            // 如果存在改执行函数则移除相应的动作
            __message[type][i] === fn && __message[type].splice(i, 1);
        }
    }
}

ok,现在就实现了一个基本的观察者模型,整理代码如下:

const Observe = (function() {
	let __message = {};
	return {
		on: function(type, fn) {
			if (typeof __message[type] === 'undefined') {
				__message[type] = [fn];
			} else {
				__message[type].push(fn);
			}
		},
		subscribe: function(type, args) {
			if (!__message[type]) return;
			let events = {
				type: type,
				args: args || {}
			};
			let	i = 0,
					len = __message[type].length;
			for (; i < len; ++i) {
				__message[type][i].call(this, events);
			}
		},
		off: function(type, fn) {
			if (__message[type] instanceof Array) {
				let i = __message.length - 1;
				for (; i >= 0; --i) {
					__message[type][i] === fn && __message[type].splice(i, 1);
				}
			}
		}
	}
})();

Observe.on('say', function(data) {
	console.log(data.args.text);
});
Observe.on('success', function() {
	console.log('success');
});
Observe.subscribe('say', {text: "hello world"});
Observe.subscribe('success');

总结

观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。

总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是具体。

使用观察者模式的好处:

  1. 支持简单的广播通信,自动通知所有已经订阅过的对象。

  2. 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。

  3. 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。

更新于 2025/2/11 22:51:58