最近在看设计模式,一本《Head First 设计模式》,一本《javascript设计模式》,两本交替着看。Head First浅显易懂,代码用java实现,理解了一个设计模式的理念以后,先想想用js如何实现,然后再看js设计模式相关章节,感觉比以前看的时候理解深入了些。
今天早上看到颜海镜同学在早读课上分享的耦合关系一文,最后一种模块间非直接耦合的实现方式第一个让我想到的就是观察者模式。正好上午没事,就写了个demo实现了一下。
非直接耦合:两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。耦合度最弱,模块独立性最强。子模块无需知道对方的存在,子模块之间的联系,全部变成子模块和主模块之间的联系。
现在要实现这样的功能:
所有模块只和主模块通讯,可以把自己的变化告知主模块,也可以从主模块接受信息并处理;
主模块负责监听所有其下的模块的变化,一旦认为该变化需要通知到指定一个或多个其他模块,就向这些模块发送消息。
大体实现思路是:
子模块包含一个主模块的引用,在实例化方法中将其对应的主模块实例作为参数赋值给自己的主模块引用。
constructor(hub) { this._hub = hub;}
子模块统一实现监听方法observeFromHub,供主模块触发执行自己的观察方法,具体由子类实现。
observeFromHub() { throw new Error('no implementation.');}
子模块统一实现将自己的变化告知主模块的方法update(value)
update(value){ this._hub.observeFromModule(value);}
主模块包含一个子模块列表
constructor(){ this.modules = [];}
子模块统一实现add/remove方法,将自己添加到主模块的子模块列表modules中。
add(hub) { var alreadyExists = hub.modules.some((el)=>el === this); if (!alreadyExists) hub.modules.push(this); return this;}remove(hub) { hub.modules = hub.modules.filter((el)=>el !== this); return this;}
主模块实现触发子模块列表中所有子模块的监听方法。
deliver(data){ this.modules.forEach((module) => module.observeFromHub(data)); return this;}
主模块实现监听方法observeFromModule,供子模块触发执行自己的观察方法,由此来感知子模块的变化,进而执行deliver方法通知其下所有子模块。
observeFromModule(data) { return this.deliver(data);}
有些方法子模块是公用的,所以可以将这些公共方法提取出来作为子模块的抽象超类
CommonModule.js
module.exports = class CommonModule { constructor(hub) { this._hub = hub; } update(value){ this._hub.observeFromModule(value); } observeFromHub() { throw new Error('no implementation.'); } add(hub) { var alreadyExists = hub.modules.some((el)=>el === this); if (!alreadyExists) hub.modules.push(this); return this; } remove(hub) { hub.modules = hub.modules.filter((el)=>el !== this); return this; }}
observeFromHub方法有子模块自己实现。这里创建两个子模块Module1和Module2。当修改Module1时,主模块通知Module2执行observerFromHub(value)方法:
Module1.js
var CommonModule = require('./CommonModule');module.exports = class Module1 extends CommonModule{ constructor(hub) { super(hub); this.inputValue = ''; } update(value) { this.inputValue = value; console.log('module1 setInput start... :' + this.inputValue); super.update(value); }}
Module2.js
var CommonModule = require('./CommonModule');module.exports = class Module2 extends CommonModule{ constructor(hub) { super(hub); this.outputValue = ''; } observeFromHub(value) { this.outputValue = value; console.log('module2 received msg : ' + this.outputValue); }}
主模块代码:
Hub.js
module.exports = class Hub { constructor(){ this.modules = []; } observeFromModule(data) { return this.deliver(data); } deliver(data){ this.modules.forEach((module) => module.observeFromHub(data)); return this; }}
客户端代码:
main.js
var Hub = require('./Hub');var Module1 = require('./Module1');var Module2 = require('./Module2');var hub = new Hub;var inputModule = new Module1(hub);var outputModule = new Module2(hub);outputModule.add(hub);inputModule.update('this is m1 speaking...');
执行main.js结果:
module1 setInput start... :this is m1 speaking...module2 received msg : this is m1 speaking...
这个例子中主模块既是观察者,观察ModuleA的变化,又是被观察者,被ModuleB观察着,A一有变化就会将信息发送给B。
还能想到的一些有趣变化:
主模块可以有多个,各自管辖的范围不同,但有些子模块可能会在多个范围中公用。
主模块中添加控制器,数据需不需要下发,下发到那几个子模块,由主模块控制。