js设计模式之【观察者模式】VS【发布/订阅模式模式】的区别?
两种模式存在一定区别
一、观察者模式(Observer)
观察者模式指的是一个对象(Subject)维持一系列依赖于它的对象(Observer),当有关状态发生变更时 Subject 对象则通知一系列 Observer 对象进行更新。
在观察者模式中,Subject 对象拥有添加、删除和通知一系列 Observer 的方法等等,而 Observer 对象拥有更新方法等等。
// 定义一个主体对象 class Subject { constructor() { this.Observers = []; } add(observer) { //添加 this.Observers.push(observer) } remove(observer) {//移除 this.Observers.filter(item => item === observer); } notify() { this.Observers.forEach(item => { item.update(); }) } } //定义观察着对象 class Observer { constructor(name) { this.name = name; } update() { console.log(`my name is:${this.name}`); } } //测试 let sub = new Subject(); let obs1 = new Observer('leaf111'); let obs2 = new Observer('leaf222'); sub.add(obs1); sub.add(obs2); sub.notify();
上述代码中,我们创建了 Subject 对象和两个 Observer 对象,当有关状态发生变更时则通过 Subject 对象的 notify 方法通知这两个 Observer 对象,这两个 Observer 对象通过 update 方法进行更新。
二、发布订阅模式(Publisher && Subscriber)
发布订阅模式指的是希望接收通知的对象(Subscriber)基于一个主题通过自定义事件订阅主题,被激活事件的对象(Publisher)通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象。
let pubSub = { subs: [], subscribe(key, fn) { //订阅 if (!this.subs[key]) { this.subs[key] = []; } this.subs[key].push(fn); }, publish(...arg) {//发布 let args = arg; let key = args.shift(); let fns = this.subs[key]; if (!fns || fns.length <= 0) return; for (let i = 0, len = fns.length; i < len; i++) { fns[i](args); } }, unSubscribe(key) { delete this.subs[key] } } //测试 pubSub.subscribe('name', name => { console.log(`your name is ${name}`); }) pubSub.subscribe('gender', gender => { console.log(`your name is ${gender}`); }) pubSub.publish('name', 'leaf333'); // your name is leaf333 pubSub.publish('gender', '18'); // your gender is 18
三、区别
如上图所示:
1、在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
2、在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
3、观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。
四、vue中的作用
Vue会遍历实例的data属性,把每一个data都设置为访问器,然后在该属性的getter函数中将其设为watcher,在setter中向其他watcher发布改变的消息。
//遍历传入实例的data对象的属性,将其设置为Vue对象的访问器属性 function observe(obj,vm){ Object.keys(obj).forEach(function(key){ defineReactive(vm,key,obj[key]); }); } //设置为访问器属性,并在其getter和setter函数中,使用订阅发布模式。互相监听。 function defineReactive(obj,key,val){ //这里用到了观察者模式,它定义了一种一对多的关系,让多个观察者监听一个主题对象,这个主题对象的状态发生改变时会通知所有观察者对象,观察者对象就可以更新自己的状态。 //实例化一个主题对象,对象中有空的观察者列表 var dep = new Dep(); //将data的每一个属性都设置为Vue对象的访问器属性,属性名和data中相同 //所以每次修改Vue.data的时候,都会调用下边的get和set方法。然后会监听v-model的input事件,当改变了input的值,就相应的改变Vue.data的数据,然后触发这里的set方法 Object.defineProperty(obj,key,{ get: function(){ //Dep.target指针指向watcher,增加订阅者watcher到主体对象Dep if(Dep.target){ dep.addSub(Dep.target); } return val; }, set: function(newVal){ if(newVal === val){ return } val = newVal; //console.log(val); //给订阅者列表中的watchers发出通知 dep.notify(); } }); } //主题对象Dep构造函数 function Dep(){ this.subs = []; } //Dep有两个方法,增加订阅者 和 发布消息 Dep.prototype = { addSub: function(sub){ this.subs.push(sub); }, notify: function(){ this.subs.forEach(function(sub){ sub.update(); }); } }