观察者模式
描述:
观察者模式(Observer Pattern)由被观察者和观察者组成,观察者可以是多个,被观察者维护着多个观察者,如添加或删除观察者;当被观察着数据变化时,会通过广播的方式通知维护的每一个观察者(即调用观察者提供的回调函数)。
概要:
观察者模式:定义了对象间一种一对多的依赖关系,当目标对象 Subject 的状态发生改变时,所有依赖它的对象 Observer 都会得到通知
举个栗子:
小美通过自己的努力吸引了2个追求者.小美每次发朋友圈必定会得到两个追求者的回应.小美与两个追求者的关系形成了观察者模式.小美是被观察者,两个舔狗是观察者.小美发朋友圈这个动作会触发舔狗的回应
// 被观察者
class ABeautifulGirl {
TIAN_GOU_LIST = [] // 舔狗们
// 添加观察者
attractTianGou(...tianGous) {
Array.prototype.push.apply(this.TIAN_GOU_LIST, tianGous)
}
// 删除观察者
kickOffTianGou(tianGou) {
const index = this.TIAN_GOU_LIST.indexOf(tianGou)
this.TIAN_GOU_LIST.splice(index, 1)
}
// 女神技能:发朋友圈(发布通知)
sendPost(data) {
this.TIAN_GOU_LIST.forEach(tianGou => {
tianGou.callback.call(tianGou, data)
})
}
}
// 观察者
class TianGou {
constructor(name, fn) {
this.name = name
this.lick = fn
}
callback(data) {
console.log(
`我是${this.name}今天女神发朋友圈说她${data},我要给女神发信息:`
)
this.lick()
}
}
// 定义两个观察者
const tiangou1 = new TianGou('tiangou1', () => {
console.log('多喝热水')
})
const tiangou2 = new TianGou('tiangou2', () => {
console.log('请一定要多喝热水呀')
})
// 小美通过自己的努力有了吸引别人关注的能力
const xiaomei = new ABeautifulGirl()
xiaomei.attractTianGou(tiangou1, tiangou2)
// 小美发了个朋友圈
xiaomei.sendPost('肚子疼')
再举个栗子
舔狗舔了数次始终没有的到小美的青睐, 这次他学会了主动, 他准备再勾搭一个女神, 于是他想办法加了小丽的微信, 开始了同时对两人的观察.
class ABeautifulGirl {
constructor(name) {
this.name = name
}
TIAN_GOU_LIST = []
sendPost(data) {
console.log(`${this.name},发个朋友圈`)
this.TIAN_GOU_LIST.forEach(tianGou => {
tianGou.callback.call(tianGou, data)
})
}
}
// 观察者:(主动)关注或取消关注
class TianGou {
constructor(name, fn) {
this.name = name
this.lick = fn
}
subscribe(publish) {
var sub = this
var alreadyExists = publish.TIAN_GOU_LIST.some(function (item) {
return item === sub
})
// 如果女神的好友名单中没有这个人,则加入进去
if (!alreadyExists) publish.TIAN_GOU_LIST.push(sub)
return this
}
unsubscribe(publish) {
var sub = this
publish.TIAN_GOU_LIST = publish.TIAN_GOU_LIST.filter(function (item) {
return item !== sub
})
return this
}
callback(data) {
console.log(
` ${this.name} 看到女神发朋友圈说她 ${data} ,我要给女神发信息:`
)
this.lick()
}
}
// 小美 & 小丽
const xiaomei = new ABeautifulGirl('xiaomei')
const xiaoli = new ABeautifulGirl('xiaoli')
// 两个舔狗
const tiangou1 = new TianGou('tiangou1', () => {
console.log(' 多喝热水')
})
const tiangou2 = new TianGou('tiangou2', () => {
console.log(' 请一定要多喝热水呀')
})
// 舔狗主动订阅女神们的消息
tiangou1.subscribe(xiaomei).subscribe(xiaoli)
tiangou2.subscribe(xiaomei)
// 小美小丽肚子疼
xiaomei.sendPost('肚子疼')
xiaoli.sendPost('腰子疼')
优缺点:
优点明显:降低耦合,两者都专注于自身功能; 缺点也很明显:所有观察者都能收到通知,无法过滤筛选;
发布订阅模式
Publisher && Subscriber
描述:
舔狗们订阅了很多女神, 但大都无疾而终, 舔狗中有一只聪明狗, 姑且就叫它二哈吧, 二哈首先意识到, 女神们生病的时候只是发 [多喝热水] 是没有用的, 还要在早上对女神说早上好, 下班了对女生下班好, 去输液了要对女神说说输的的想你的夜. 二哈开始行动了, 于是二哈学习了发布订阅模式.
概要:
发布订阅模式:基于一个事件(主题)通道,希望接收通知的对象 Subscriber 通过自定义事件订阅主题,被激活事件的对象 Publisher 通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象。 发布订阅模式与观察者模式的不同,“第三者” (事件中心)出现。目标对象并不直接通知观察者,而是通过事件中心来派发通知。
举个栗子:
const GirlsHub = function () {
this.grils = {}
this.on = function (name, cb) {
if (this.grils[name]) {
this.grils[name].push(cb)
} else {
this.grils[name] = [cb]
}
}
this.trigger = function (name, ...arg) {
if (this.grils[name]) {
this.grils[name].forEach(eventListener => {
eventListener(...arg)
})
}
}
}
let girls = new GirlsHub()
girls.on('morring', () => {
console.log('对小美说早安')
})
girls.on('morring', () => {
console.log('对小丽说早安')
})
girls.on('morring', () => {
console.log('对小美丽说早安')
})
girls.on('noon', () => {
console.log('对小美说午安')
})
girls.on('noon', () => {
console.log('对小丽说午安')
})
girls.on('noon', () => {
console.log('对小美丽说午安')
})
girls.on('evening', () => {
console.log('对小美说晚安')
})
girls.on('evening', () => {
console.log('对小丽说晚安')
})
girls.on('evening', () => {
console.log('对小美丽说晚安')
})
girls.trigger('morring')
girls.trigger('noon')
girls.trigger('evening')
学会之后,二哈化被动为主动, 每到时辰就开始给女神们发消息, 本以为能够收获女神们的芳心,到后来发现,自己被一半人都拉黑了.
看来功力还不够深厚啊.需要学习以下其他的设计模式来优化以下方案。
优缺点:
优点:解耦更好,细粒度更容易掌控;
缺点:不易阅读,额外对象创建,消耗时间和内存
两种模式的关联和区别
发布订阅模式更灵活,是进阶版的观察者模式,指定对应分发。
- 观察者模式维护单一事件对应多个依赖该事件的对象关系;
- 发布订阅维护多个事件(主题)及依赖各事件(主题)的对象之间的关系;
- 观察者模式是目标对象直接触发通知(全部通知),观察对象*接收通知。发布订阅模式多了个中间层(事件中心),由其去管理通知广播(只通知订阅对应事件的对象);
- 观察者模式对象间依赖关系较强,发布订阅模式中对象之间实现真正的解耦。