有时候面试的时候可能会被问到:
观察者模式和发布订阅模式的区别?
没有区别吧?
好像有区别吧?
我们首先来看一下“观察者模式”和“发布订阅模式”
一、观察者设计模式
理解设计模式在设计之初的是为了解决什么问题就能很好的在写代码中运用不同的设计模式。
所谓的观察者模式,其实为了实现松耦合(loosely coupled),也就是为了解耦。
当数据有变化有更新的时候,某个方法被调用,这时候就可以更新其他地方需要用到这个数据的地方。
举个例子:
以气象站为例,每当气象站测试数据有更新的时候,changed() 方法都会被调用,于是我们在 changed() 方法中,更新气象仪器上的数据,比如温度、气压等等。
这样写没有毛病,但是,如果我们以后再 changed() 方法调用的时候,更新更多的信息,比如说湿度,这个时候我们需要去修改 changed() 方法的代码,这个就是紧耦合的坏处。
那怎么解决这个紧耦合的问题呢?
观察者模式中,changed() 方法所在的实例对象,就是被观察者(subject),只需要维护一套观察者(observer)的集合,这些 observer 实现相同的接口,subject 只需要知道:通知观察者(observer)时,需要调用哪一个统一方法就行。
我们再来看一下简单的例子
function Subject(){this.observerData = [];
}
Subject.prototype = {add: function(observer) {this.observerData.push(observer);
},
remove:function(observer) {
var observerData = this.observerData;
for(let i=0,length=observerData.length;i<length;i++){if(observerData[i] === observer){observerData.splice(i,1)
}
}
},
notify: function() {
var observerData = this.observerData;
for(let i=0,length=observerData.length;i<length;i++){observerData[i].update();}
}
}
function Observer(name){this.name = name;}
Observer.prototype = {update: function() {console.log('hello,' + this.name);
}
}
var sub = new Subject();
var obj1 = new Observer('saucxs');
var obj2 = new Observer('songEagle');
sub.add(obj1);
sub.add(obj2);
sub.notify();
这时候输出的是
hello,saucxs
hello,songEagle
上述代码中,我们创建了 Subject 对象和两个 Observer 对象,当前状态发生变更的时候则通过 Subject 对象的 notify 方法通知两个 Observer 队形,这两个 Observer 对象通过 update 方法进行更新。
二、发布订阅模式(Publisher && Subscriber)
其实觉得发布订阅模式里的 Publisher,就是观察者模式的 Subject,而 Subscriber 就是 Observer。Publisher 变化的时候,就会主动去通知 Subscriber。
其实并不是这样的。
在发布订阅模式里,发布者并不会直接通知订阅者,换句话说,发布者和订阅者,彼此互补相识。
那他们之间如何进行通信的?是通过第三者,就是在消息队列中,我们常说的经纪人 Broker。
发布者只需要告诉 Broker,我要发消息。topic 是“saucxs”
订阅者只需要告诉 Broker,我要订阅 topic 是“saucxs”
于是,当 Broker 接收到发布者发来的消息,并且 topic 是 saucxs 的时候,就会把消息推送到订阅了 topic 的 saucxs 的订阅者,也可能是订阅者自己过来拉取,具体看代码实现。
也就是说,发布订阅模式中,发布者和订阅者,不是松耦合,是完全的解耦的。
放一张很简单的图,对比一下两个模式的区别:
三、总结
表面上看:
1、观察者模式中,两个角色:观察者和被观察者
2、发布订阅模式中,三个角色:发布者,订阅者,经纪人
深层次的看:
1、观察者模式:观察者和被观察者,是松耦合的关系
2、发布订阅模式:发布和订阅者是完全不存在耦合的
从使用层面上看:
1、观察者模式:多用于单个应用内部,维护的是单一事件对应多个依赖的
event -> [obj1,obj2,obj3,….]
2、发布订阅模式:多用于跨应用的模式,比如我们说的消息的中间件,维护的是多个事件以及依赖的
event1 -> [obj1,obj2,…]
event2 -> [obj1,obj3,…]