序、设计模式分类
一、装饰者模式(原型链)
二、策略模式
三、代理模式
四、发布订阅模式
五、迭代器模式
六、工厂模式
七、外观模式
八、状态模式
九、单例模式
十、适配器模式
一、概述
观察者模式: 观察者(Observer)直接订阅(Subscribe)主题(Subject),而当主题被激活的时候,会触发(Fire Event)观察者里的事件。
发布订阅模式: 订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Topic),当发布者(Publisher)发布该事件(Publish topic)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。
1. 观察者模式
- 首先定义一个数组存储订阅列表(数组存函数)
- 定义订阅函数,函数参数:订阅对象与订阅信息。函数体负责将订阅信息(为一个函数)存入订阅数组列表。
- 定义发布任务函数,函数接受订阅信息参数。函数体内遍历订阅信息,以遍能够找到匹配的订阅信息。
主要维护一个存储函数的数组,通过入栈的方式维护订阅信息,通过数组遍历的方式使用订阅列表,以找到匹配的订阅信息。
function Hunter(name, level) {
this.name = name;
this.level = level;
this.list = [];
}
Hunter.prototype.publish = function(money) {
console.log(this.level + '猎人' + this.name + '寻求帮助');
this.list.forEach(function(item, index) {
item(money);
})
}
Hunter.prototype.subscribe = function(target, fn) {
console.log(this.level + '猎人' + this.name + '订阅了' + target);
target.list.push(fn); // 向目标猎人list中推入订阅
}
let hounterMing = new Hunter('小明','黄金');
let hounterJin = new Hunter('小金','黄金');
let hounterZhang = new Hunter('校长', '黄金');
let hounterPeter = new Hunter('Peter', '黄铜');
hunterMing.subscribe(hunterPeter, function(money){
console.log('小明表示:' + (money > 200 ? '' : '暂时很忙,不能') + '给予帮助')
})
hunterJin.subscribe(hunterPeter, function(){
console.log('小金表示:给予帮助')
})
hunterZhang.subscribe(hunterPeter, function(){
console.log('小金表示:给予帮助')
})
hunterPeter.publish(198)
2. 发布订阅模式
观察者模式和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心。
观察者模式由具体目标调度,每个被订阅的目标里面都需要有对观察者的处理,这种处理方式比较直接粗暴,但是会造成代码的冗余。
而发布订阅模式中统一由调度中心进行处理,订阅者和发布者互不干扰,消除了发布者和订阅者之间的依赖。这样一方面实现了解耦,还有就是可以实现更细粒度的一些控制。比如发布者发布了很多消息,但是不想所有的订阅者都接收到,就可以在调度中心做一些处理,类似于权限控制之类的。还可以做一些节流操作。
通俗版本:
- 发布订阅模式就是将原本属于被订阅者维护的订阅列表独立出来,增加了一个专门处理订阅信息的订阅中心。实际上依然是维护一个数组列表。
- 发布者和订阅者的发布和订阅的具体内部逻辑被订阅中心接管,只需要调用订阅中心的函数即可。
/**
* HunterUnion 负责任务函数的存储,实现形式为对象数组的入栈和遍历。
* 内部数据结构为 :
* topics: {
* 'tiger1': [functionA(), functionB, function C],
* 'tiger2': [functionA(), functionB, function C],
* }
*/
let HunterUnion = {
type: 'hunt',
topics: Object.create(null),
subscribe: function (topic, fn){
if(!this.topics[topic]){
this.topics[topic] = [];
}
this.topics[topic].push(fn);
},
publish: function (topic, money){
if(!this.topics[topic])
return;
for(let fn of this.topics[topic]){
fn(money)
}
}
}
// Hunter只负责存储基本的订阅信息
function Hunter(name, level) {
this.name = name;
this.level = level;
}
Hunter.prototype.subscribe = function(topic, fn) {
console.log(this.level + '猎人' +
this.name + '订阅了狩猎' + topic + '的任务');
HunterUnion.subscribe(topic, fn);
}
Hunter.prototype.publish = function(topic, money) {
console.log(this.level + '猎人' +
this.name + '发布了狩猎' + topic + '的任务');
HunterUnion.publish(topic, money);
}
//猎人工会走来了几个猎人
let hunterMing = new Hunter('小明', '黄金')
let hunterJin = new Hunter('小金', '白银')
let hunterZhang = new Hunter('小张', '黄金')
let hunterPeter = new Hunter('Peter', '青铜')
hunterMing.subscribe('tiger', function(money){
console.log('小明表示:' + (money > 200 ? '' : '不') + '接取任务')
})
hunterJin.subscribe('tiger', function(money){
console.log('小金表示:接取任务')
})
hunterZhang.subscribe('tiger', function(money){
console.log('小张表示:接取任务')
})
//Peter订阅了狩猎sheep的任务
hunterPeter.subscribe('sheep', function(money){
console.log('Peter表示:接取任务')
})
//Peter发布了狩猎tiger的任务
hunterPeter.publish('tiger', 198)
3. 观察者模式是不是发布订阅模式
网上关于这个问题的回答,出现了两极分化,有认为发布订阅模式就是观察者模式的,也有认为观察者模式和发布订阅模式是真不一样的。
其实我不知道发布订阅模式是不是观察者模式,就像我不知道辨别模式的关键是设计意图还是设计结构(理念),虽然《JavaScript设计模式与开发实践》一书中说了分辨模式的关键是意图而不是结构。
如果以结构来分辨模式,发布订阅模式相比观察者模式多了一个中间件订阅器,所以发布订阅模式是不同于观察者模式的;如果以意图来分辨模式,他们都是实现了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新,那么他们就是同一种模式,发布订阅模式是在观察者模式的基础上做的优化升级。
不过,不管他们是不是同一个设计模式,他们的实现方式确实有差别,我们在使用的时候应该根据场景来判断选择哪个。