解决模块间耦合的设计模式
2017-08-09 22:19
211 查看
大家都知道,模块间的耦合不利于代码的可读性和可维护性,好的代码一般都会按功能或者别的将代码模块化,那么模块化的代码之间怎么通信呢?下面来介绍两种用于模块间解耦的设计模式。
观察者模式有一个消息容器,和三个方法,分别是订阅信息方法、取消订阅的信息方法、发送订阅的消息方法。
例如:
接受两个参数:消息类型以及相应的处理动作。
代码如下:
发布消息方法,其功能是当观察者发布一个消息时将所有订阅者订阅的消息一次执行。
接收两个参数:消息类型及动作执行时需要传递的参数。
消息注销方法:将订阅者注销的消息从消息队列中清除。
接收两个参数:消息类型及执行某一动作。
整体代码如下:
View Code
运行结果如下:
本质是分装多个对象的交互,并且这些对象的交互一般都是在中介者内部实现的。
代码实现如下:
测试代码如下:
用户收藏导航模块有消息提醒和导航网址功能;
推荐用户导航有推荐用户导航内的导航有消息提醒功能;
最近常用导航有最近常用导航栏内有导航网址功能;
代码实现:
用户收藏导航模块
推荐用户导航
最近常用导航
设置层模块
demo地址:http://jsrun.net/QDYKp/show
中介者模式中的消息的发送方只有一个,就是中介者对象,中介者对象不能订阅消息,只有那些活跃对象(订阅者)才可以订阅中介者的消息。也可以将中介者模式看作将消息系统封装在中介者对象内部,中介者对象只能是消息的发送者。
中介者模式与观察者模式比较:
都是通过消息传递实现对象间或模块间的解耦。
观察者中的订阅者是双向的,既可以是消息的发布者,也可以是消息的订阅者。
中介者模式中,订阅者是单项的,只能是消息的订阅,消息同一有中介者对象发布,所有订阅者对象间接地被中介者管理。
观察者模式
概念介绍
观察者模式也被称作消息机制或发布-订阅者模式,为了解决主题对象与观察者之间功能的耦合。观察者模式有一个消息容器,和三个方法,分别是订阅信息方法、取消订阅的信息方法、发送订阅的消息方法。
例如:
/* * 将观察者放在闭包中,当页面加载就立即执行 */ 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); } } }
整体代码如下:
// 对象间的解耦 class Student { constructor(result) { const self = this; self.result = result; self.say = function() { console.log(self.result); } } answer(question) { // 注册参数问题 Observer.register(question,this.say); } sleep(question) { console.log(`${this.result} ${question}已被注销`); // 取消对老师问题对监听 Observer.remove(question, this.say); } } class Teacher { constructor() { } ask(question) { console.log(`问题是 ${question}`); // 发布提问消息 Observer.fire(question); } } // 实例 const student1 = new Student('学生1回答问题'); const student2 = new Student('学生2回答问题'); const student3 = new Student('学生3回答问题'); const teacher = new Teacher(); // 学生监听老师提问问题 student1.answer('什么是设计模式?'); student1.answer('简述观察者模式'); student2.answer('什么是设计模式?'); student3.answer('什么是设计模式?'); student3.answer('简述观察者模式'); // 老师提问问题 teacher.ask('什么是设计模式?'); // 学生3在'简述观察者模式的时候睡着了' student3.sleep('简述观察者模式'); teacher.ask('简述观察者模式');
View Code
运行结果如下:
中介者模式
概念和实现
中介者模式通过模块间或者对象间的复杂通信,来解决模块或对象间的耦合。本质是分装多个对象的交互,并且这些对象的交互一般都是在中介者内部实现的。
代码实现如下:
/** * 中介者对象 */ const Mediator = (function() { // 消息对象 let _msg = {}; return { /** * 订阅消息方法 * @param type 消息名称 * @param action 消息回调函数 */ register(type, action) { // 如果消息存在 if(_msg[type]) { // 存入回调函数 _msg[type].push(action); } else { // 不存在,则建立该消息容器 _msg[type] = [action]; } }, /** * 发布消息方法 * @param type 消息名称 */ send(type) { // 如果该消息已经被订阅 if(_msg[type]) { // 遍历已存储的消息回调函数 for(let i = 0, len = _msg[type].length; i < len; i++) { // 执行该回调函数 _msg[type][i] && _msg[type][i](); } } } } })();
测试代码如下:
// 订阅demo消息,执行回调函数--输出first Mediator.register('demo',()=> { console.log('first'); }); // 订阅demo消息,执行回调函数--输出second Mediator.register('demo', ()=> { console.log('second'); }); // 发布demo消息 Mediator.send('demo');
使用场景
添加一个用户首页导航设置,导航上面有消息提醒和导航网址。具体如下:用户收藏导航模块有消息提醒和导航网址功能;
推荐用户导航有推荐用户导航内的导航有消息提醒功能;
最近常用导航有最近常用导航栏内有导航网址功能;
代码实现:
用户收藏导航模块
(function(){ // 订阅隐藏用户收藏导航消息提醒消息 Mediator.register('hideAllNavNum', () => { showHideNavWidget('collectionNav', 'b', false); }); // 订阅显示用户收藏导航消息提醒消息 Mediator.register('showAllNavNum', () => { showHideNavWidget('collectionNav', 'b', true); }); // 订阅隐藏用户收藏导航消息提醒消息 Mediator.register('hideAllNavUrl', () => { showHideNavWidget('collectionNav', 'span', false); }); // 订阅显示用户收藏导航消息提醒消息 Mediator.register('showAllNavUrl', () => { showHideNavWidget('collectionNav', 'span', true); }); })();
推荐用户导航
(function() { // 订阅隐藏推荐用户导航消息提醒消息 Mediator.register('hideAllNavNum', () => { showHideNavWidget('recommendNav', 'b', false); }); // 订阅显示推荐用户导航消息提醒消息 Mediator.register('showAllNavNum', () => { showHideNavWidget('recommendNav', 'b', true); }); })();
最近常用导航
(function() { // 订阅隐藏最近常用导航消息 Mediator.register('hideAllNavUrl', () => { showHideNavWidget('recentlyNav', 'span', 'hide'); }); // 订阅显示最近常用导航消息 Mediator.register('showAllNavUrl', () => { showHideNavWidget('recentlyNav', 'span', 'show'); }); })();
设置层模块
(function () { // 消息提醒选框 const hideNum = document.getElementById('hideNum'); // 网址选框 const hideUrl = document.getElementById('hideUrl'); // 消息提醒选框事件 hideNum.addEventListener('change',function(){ if(this.checked){ // 中介者发布隐藏消息提醒功能消息 Mediator.send('hideAllNavNum'); } else { // 中介者发布隐藏消息提醒功能消息 Mediator.send('showAllNavNum'); } },false); // 网址选框事件 hideUrl.addEventListener('change',function() { if(hideUrl.checked) { // 中介者发布隐藏所有网址功能消息 Mediator.send('hideAllNavUrl'); } else { // 中介者发布显示所有网址功能消息 Mediator.send('showAllNavUrl'); } }, false); })();
demo地址:http://jsrun.net/QDYKp/show
总结
观察者模式最主要的作用是解决类和对象之间的耦合,解耦两个相互依赖的对象,使其依赖于观察者的消息机制。对于任意一个订阅者对象来说,其他订阅者对象的改变不会影响到自身。对于每一个订阅者来说,其自身既可以是消息的发出者也可以是消息的执行者。中介者模式中的消息的发送方只有一个,就是中介者对象,中介者对象不能订阅消息,只有那些活跃对象(订阅者)才可以订阅中介者的消息。也可以将中介者模式看作将消息系统封装在中介者对象内部,中介者对象只能是消息的发送者。
中介者模式与观察者模式比较:
都是通过消息传递实现对象间或模块间的解耦。
观察者中的订阅者是双向的,既可以是消息的发布者,也可以是消息的订阅者。
中介者模式中,订阅者是单项的,只能是消息的订阅,消息同一有中介者对象发布,所有订阅者对象间接地被中介者管理。
相关文章推荐
- Java中利用反射的思想解决工厂设计模式的耦合问题
- 设计模式GRASP和GoF是怎样解决耦合的问题
- 学习笔记-设计模式GRASP和GoF是怎样解决不同情况的耦合问题
- Java之------单机版书店管理系统(设计思想和设计模式系列七)库存模块
- 设计模式就是来解决应变---原型(prototype)
- 设计模式——Module(模块)模式
- PHP耦合设计模式的理解
- 关于stm32f4xx的片上外设I2C模块用作主模式下BUSY位总是置1的解决方法
- 解决单例设计模式中懒汉式线程安全问题
- C++性能系列之高内聚低耦合的设计模式
- 软考(三)模块设计原则之内聚和耦合
- 基本上,把switch,用设计模式代替,肯定是bug和过度设计。想想,本来修改一个文件几行代码可以解决的问题,变成修改3-6个类才能实现一样的功能。不是傻是什么?
- JAVA 学习模块十: 设计模式
- 学习设计模式的层次及设计模式怎么解决设计问题
- 设计模式怎样解决设计问题[笔记]
- 设计模式怎么解决设计问题
- 解决Eclipse再次打开工程时找不到Design设计模式的问题!...
- 【设计模式】责任链2-更好的解决问题
- Tip:解决DesignMode不能正确反应是否处于设计模式的问题
- 不使用mutex设计模式解决并发访问cache