发布订阅模式
发布订阅
- 发布订阅是一种消息范式,消息的发布者不会直接将消息发送给订阅者,而是将消息分为不同类别,也无需知道有哪些订阅者。
- 同样订阅者可以选择一个或多个尽心消息订阅,只接收感兴趣的消息,不用知道发布者的存在及有多少消息可以订阅。
主要是创建一个类似于这样的对象{ “click”:[‘fn1’, ‘fn2’, …], “change”:[‘fn1’, …] }
主要分三步
- 发布订阅模式要有消息管理,或者事件管理中心
- 事件发布的方法$emit
- 事件订阅的方法$on
class EventEmit{
constructor(){
this.subs = Object.create(null)
}
$on(eventType, handler){
this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(handler)
}
$emit(eventType){
if(this.subs[eventType]){
this.subs.forEach( handler => {
handler()
})
}
}
}
// 测试案例
const em = new EventEmit()
em.$on("click", ()=>{
console.log("click1...")
})
em.$on("click", ()=>{
console.log("click2...")
})
em.$emit("click")
通过网站、体育比赛、粉丝完成发布订阅
代码示例 ```javascript // 体育网站,事件处理中心 class Website { constructor() { this._events = {}; } // 订阅 subscribe(type, fn) { if (this._events[type]) { this._events[type].push(fn); } else { this._events[type] = [fn]; } } // 发布 publish(type) { let listeners = this._events[type]; let args = Array.prototype.slice.call(arguments, 1); if (listeners) { listeners.forEach((listener) => {
}); } } // 移除事件 remove(type) { if (!this._events[type]) { return false; } else { this._events[type].length = 0; } } }listener(...args);
class Star { constructor(name) { this.name = name; } // 有体育比赛事件 event(type, address, time) { website.publish(type, address, time); } }
class Fan {
constructor(name) {
this.name = name;
}
see(type, website) {
website.subscribe(type, (address, time) => {
console.log(
${time}在${address}有一场${type}比赛,欢迎${this.name}前去观看
);
});
}
}
let website = new Website();
website.subscribe(“篮球”, (address, time) => {
${time}在${address}有一场体育比赛
;
});
website.subscribe(“羽毛球”, (address, time) => {
${time}在${address}有一场体育比赛
;
});
let fan1 = new Fan(“tom”);
let fan2 = new Fan(“sam”);
fan1.see(“篮球”, website);
fan2.see(“羽毛球”, website);
let star = new Star(); star.event(“篮球”, “上海”, “10.1”); let star2 = new Star(); star2.event(“篮球”, “新加坡”, “10.1”); star2.event(“羽毛球”, “新加坡”, “10.1”); // 可以移除监听的事件,移除后不再触发 website.remove(“羽毛球”); star2.event(“羽毛球”, “北京”, “10.1”);
网站是体育比赛和粉丝之间的一个事件管理中心,网站可以发布体育比赛消息通知粉丝,粉丝接收消息,体育明星可以发布消息给网站。这样解耦了明星和粉丝之间的直接关联。
> Vue中的EventBus实现消息通信使用的发布订阅模式。通过创建一个事件中心EventBus,
> - 通过$on进行事件监听
> - 通过$emit进行事件触发
<a name="hGgDn"></a>
## 观察者模式
> 观察者模式提供了对象间一种一对多的依赖关系,一个对象【被观察者Dep】发生变化,所有依赖它的对象【watcher】将收到通知,**并自动更新**。
> 观察者模式属于行为模式,关注的是对象之间的通信。
- 被观察者(目标Dep)维护观察者的一系列方法
- 观察者提供更新接口
- 观察者把自己注册到被观察者里
- 被观察者发生变化时,调用观察者的更新方法
<a name="KbwYl"></a>
### 类图
![Snipaste_2020-09-12_23-27-31.png](https://cdn.nlark.com/yuque/0/2020/png/737887/1599924473106-b4503550-6351-4ad8-9328-c2c46d8e2469.png#crop=0&crop=0&crop=1&crop=1&height=301&id=QIxZb&margin=%5Bobject%20Object%5D&name=Snipaste_2020-09-12_23-27-31.png&originHeight=301&originWidth=777&originalType=binary&ratio=1&rotation=0&showTitle=false&size=77773&status=done&style=none&title=&width=777)
<a name="UHKIM"></a>
### 代码示例
```javascript
class Star {
constructor(name) {
this.name = name;
this.state = "";
this.fans = [];
}
getState() {
return this.state;
}
setState(state) {
this.state = state;
this.notify();
}
//添加粉丝
addFan(fan) {
this.fans.push(fan);
}
//通知粉丝
notify() {
if (this.fans.length > 0) {
this.fans.forEach((fan) => {
fan.update();
});
}
}
}
class Fan {
constructor(name, star) {
this.name = name;
this.star = star;
this.star.addFan(this);
}
update() {
console.log(
`${this.name} 看${this.star.name}${this.star.getState()}的比赛`
);
}
}
let star = new Star("姚明");
let f1 = new Fan("小明", star);
star.setState("在上海体育场");
star.setState("在洛杉矶体育场");
let starLin = new Star("林丹");
let f2 = new Fan("小刚", starLin);
starLin.setState("在新加坡体育场");
vue中的观察者模式
class Dep{
constructor(){
this.subs = []
}
addSub(sub){
if(sub && sub.update){
this.subs.push(sub)
}
}
notify(){
this.subs.forEach( sub =>{
sub.update()
})
}
}
class Watcher{
update(){
console.log("update....")
}
}
// 测试案例
let dep = new Dep()
let watcher = new Watcher()
dep.addDep(watcher)
dep.notify()
观察者和发布订阅模式的区别
- 发布订阅模式由统一的调度中心调用,因此发布者和订阅者不需要知道对方的存在
- 观察者模式由具体目标调度,比如当事件触发,Dep就会去调用观察者的方法,所以观察者模式的订阅者与发布者之间存在着依赖关系
举例对比发布订阅和观察者模式
首先是观察者模式
房东是被观察者,租客是观察者,租客关注着房东的优质房源信息的变动。有变化了立刻租下来。该模式存在一定的问题,房东和租客直接强耦合关系,租客关注的房源有可能一直没有空出来,就要一直等下去。租客关注的房屋信息少,没有关注更多的房屋信息。
发布订阅模式
发布订阅模式引入了房屋中介,第三方事件中心,该中心有多套房源,有多个房东,有多个租客,可以对事件进行调度管理。解决了房东和租客直接的强耦合关系。
举个生活例子:
- 观察者模式:公司给自己员工发福利,是由公司的行政部门发送,这件事不适合交给第三方,原因是“公司”和“员工”是一个整体。存在耦合关系。
- 发布-订阅模式:公司要给其他人发各种快递,因为“公司”和“其他人”是独立的,其唯一的桥梁是“快递”,这件事适合交给第三方快递公司解决,公司和其它人之间没有耦合关系。
上述过程中,如果公司自己去管理快递的配送,那公司就会变成一个快递公司,业务繁杂难以管理,影响公司自身的主营业务,因此使用何种模式需要考虑什么情况两者是需要耦合的。
- 在观察者模式中,观察者是知道Subject的,Subject【被观察者】一直保持对观察者进行记录。在发布订阅模式中,发布者和订阅者不知道对方的存在,它们只有通过事件中心的消息代理进行通信。
- 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
- 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。发布-订阅模式大多数时候是异步的(使用消息队列)