JS设计模式之观察者模式

302 阅读4分钟

什么是观察者模式

  观察者模式(Observer):又被称为发布-订阅模式或消息机制,定义了一种一对多的依赖关系,只要被观察对象发生改变,依赖于它的观察者对象就会收到通知并自动被更新,解决了主体对象(被观察对象)与观察者之间功能的耦合。

通俗易懂地介绍观察者模式

  单看定义可能不是很明白,下面举一个通俗易懂的例子。   你到商店去买东西,刚好你要买的东西没有货了,于是你在店员那里留了电话。过了几天,店里有货了,那名店员就打电话给你,然后你接到电话就去店里面取货了。   这个例子里面,电话号码就是那条消息;你把电话号码给店员,就是注册消息;店员后来给你打电话,叫你回去取,就是发布消息;你接到电话,回去取就是观察者接受了消息,进行了后续处理。

实现观察者模式

  首先,我们需要创建一个观察者对象,包括一个消息容器、3个方法,分别是订阅消息方法、发送订阅方法和取消订阅方法。

var Observer = (function () {
    // 使用闭包,是为了防止消息队列被篡改
    var __messages = {};
    return {
        // 注册消息接口
        regist: function () {},
        // 发布消息接口
        fire: function () {},
        // 移除消息接口
        remove: function () {},
    };
})();

注册消息方法

  注册消息方法的作用是将订阅者注册的消息推入到消息队列中,因此我们需要接受两个参数,分别是消息类型和相应的处理方法。这里要分2中情况:1.如果消息类型不存在,就需要创建一个消息类型,并将处理方法放入该消息类型;2.如果消息类型存在,则直接将处理方法放入该消息类型的消息队列中。

// 注册消息方法
regist: function (type, fn) {
	if (typeof __messages[type] === "undefined") {
		__messages[type] = [fn];
	} else {
		__messages[type].push(fn);
	}
}

发布消息方法

  发布消息方法的功能就是当观察者发布一个消息是,将所有订阅者订阅的消息依次执行。这里也需要传递两个参数,分别是消息类型和动作执行时的参数,其中消息类型是必须的。在执行消息队列之前要校验该消息类型的消息队列是否存在,存在则依次执行消息队列中的方法。

// 发布消息方法
fire: function (type, args) {
	if (!__messages[type]) {
		return;
	}
	var events = {
			type: type, // 消息类型
			args: args, // 消息携带数据
		},
		len = __messages[type].length; // 消息队列长度
	
	for (let i = 0; i < len; i++) {
		__messages[type][i].call(this, events);
	}
}

移除消息方法

  移除消息方法就是将订阅者注册的消息从消息队列中清除,因此我们需要传递两个参数,即消息类型和执行队列中的某一方法。当然为了避免删除消息时,消息队列中不存在的情况,需要对消息队列中消息的存在进行校验。

// 注销消息方法
remove: function (type, fn) {
	// 如果消息执行队列存在
	if (__messages[type] instanceof Array) {
		let index = removeMessages.indexOf(fn);
		if (index){
			removeMessages.splice(index, 1);
		}
		// 从最后一个消息方法开始遍历
		// var i = __messages[type].length - 1;
		// for (; i >= 0; i--) {
		// 	__messages[type][i].toString().replace(fn, '$1') === fn.toString().replace(fn, '$1')  && __messages[type].splice(i, 1);
		// }
	}
}

  到此,我们就完成了一个观察者模型,现在就去实操一下吧。

Observer.regist("test", function (e) {
    console.log(e.type, e.args.msg);
});
Observer.regist("test", function (e) {
    console.log("hello", e.args.name);
});
Observer.fire("test", { msg: "传递参数1", name: "Eillen" });
/** 结果
 * test 传递参数1
 * hello Eillen
*/

Observer.remove("test", function (e) {
    console.log("hello", e.args.name);
});
Observer.fire("test", { msg: "传递参数2",  name: "Jack"});
/** 结果
 * test 传递参数2
*/

至此,我们就已经实现了观察者模式,为自己点个赞吧,☺️

应用场景

  redux中就应用到了观察者模式,下面的实例是redux的核心代码。如果看不懂,可以去学习一下8k字 | Redux/react-redux/redux中间件设计实现剖析中的redux知识。

import { reducer } from './reducer'
export const createStore = (reducer) => {        
    let currentState = {}        
    let listeners = []  //消息队列        
    function getState() {                
        return currentState        
    }        
    function dispatch(action) {                
        currentState = reducer(currentState, action)                
        listeners.forEach(listener => listener())        
    }        
    function subscribe(listener) {                
        listeners.push(listener)  
        return function unsubscribe(){
            const index = listeners.indexOf(listener);
            listeners.splice(index, 1);
        }      
    }        
    dispatch({ type: '@@REDUX_INIT' })  //初始化store数据        
    return { getState, subscribe, dispatch }
}

解析:

  subscribe就是上面register方法,用来注册消息;dispatch就是上面的fire方法,用来发布消息;subscribe执行后的unsubscribe方法就是取消订阅的方法。