大家都知道,模块间的耦合不利于代码的可读性和可维护性,好的代码一般都会按功能或者别的将代码模块化,那么模块化的代码之间怎么通信呢?下面来介绍两种用于模块间解耦的设计模式。
概念介绍
观察者模式也被称作消息机制或发布-订阅者模式,为了解决主题对象与观察者之间功能的耦合。
观察者模式有一个消息容器,和三个方法,分别是订阅信息方法、取消订阅的信息方法、发送订阅的消息方法。
例如:
/* * 将观察者放在闭包中,当页面加载就立即执行 */ const Observer = (function () { // 防止消息队列暴露而被篡改故将消息容器作为静态私有变量保存 let _messages = {}; return { // 注册信息接口 register() { }, // 发布信息接口 fire() { }, // 移除信息接口 remove() { } } })();
方法实现
注册方法的作用是将订阅者注册的消息推入到消息队列中。
接受两个参数:消息类型以及相应的处理动作。
代码如下:
// 注册信息接口 register(type, fn) { // 如果此消息不存在则应该创建一个该消息类型 if(typeof _messages[type] === 'undefined') { // 将动作推入到该消息对应的动作执行队列中 _messages[type] = [fn]; // 如果此消息存在 } else { // 将动作方法推入该消息对应的动作执行序列中 _messages[type].push(fn); } }
发布消息方法,其功能是当观察者发布一个消息时将所有订阅者订阅的消息一次执行。
接收两个参数:消息类型及动作执行时需要传递的参数。
// 发布信息接口 fire(type, args) { // 如果该消息没有被注册,则返回 if(!_messages[type]) { return; } // 定义消息信息 const events = { type: type, // 消息类型 args: args || {} // 消息携带数据 }; // 遍历消息动作 for(let i = 0, len = _messages[type].length; i < len; i++) { // 依次执行注册的消息对应的动作序列 _messages[type][i].call(this, events); } }
消息注销方法:将订阅者注销的消息从消息队列中清除。
接收两个参数:消息类型及执行某一动作。
// 移除信息接口 remove(type, fn) { // 如果消息动作队列存在 if(_messages[type] instanceof Array) { // 从最后一个消息动作遍历 for(let i = _messages[type].length - 1; i >= 0; i--) { // 如果存在该动作则在消息动作序列中移除相应动作 _messages[type][i] === fn && _messages[type].splice(i, 1); } } }
整体代码如下:
/* * 将观察者放在闭包中,当页面加载就立即执行 */ const Observer = (function () { // 防止消息队列暴露而被篡改故将消息容器作为静态私有变量保存 let _messages = {}; return { // 注册信息接口 register(type, fn) { // 如果此消息不存在则应该创建一个该消息类型 if(typeof _messages[type] === 'undefined') { // 将动作推入到该消息对应的动作执行队列中 _messages[type] = [fn]; // 如果此消息存在 } else { // 将动作方法推入该消息对应的动作执行序列中 _messages[type].push(fn); } }, // 发布信息接口 fire(type, args) { // 如果该消息没有被注册,则返回 if(!_messages[type]) { return; } // 定义消息信息 const events = { type: type, // 消息类型 args: args || {} // 消息携带数据 }; // 遍历消息动作 for(let i = 0, len = _messages[type].length; i < len; i++) { // 依次执行注册的消息对应的动作序列 _messages[type][i].call(this, events); } }, // 移除信息接口 remove(type, fn) { // 如果消息动作队列存在 if(_messages[type] instanceof Array) { // 从最后一个消息动作遍历 for(let i = _messages[type].length - 1; i >= 0; i--) { // 如果存在该动作则在消息动作序列中移除相应动作 _messages[type][i] === fn && _messages[type].splice(i, 1); } } } } })();