设计模式的目的是为了改善代码
一、概念:
1、发布订阅者模式(也叫观察者模式),是一对多的关系。举个例子:比如开学了。
老师【发布者】 向 学生【订阅者】 发个开学通知,学生收到通知就会各自行动起来。这就是一个发布订阅者模式。
说明:在老师那里有登记的学生才会收到通知。即有订阅的对象才会收到通知。
二、讲解:
1、没用设计模式时,我们的代码可能经常是这样的。
//发布者 let teacher = { start(){ console.log('开学了') console.log('张三,来报道') console.log('李四,来报道') console.log('王五,来报道') } } teacher.start()
说明:一个函数被调用, 函数里面的代码开始执行。如果函数里面的代码执行的是不同功能,代码逻辑比较混乱。需求改动时,这块代码就会很难维护。
2、上面代码改进下,函数里面的代码,分不同的功能放在对应的函数里面。这样功能 模块 就 比较清晰了。
let teacher = { start(){ console.log('开学了') notice_zs() // 执行函数可以看做通知对象。函数里面的程序,就是 通知 收到后,各自 执行的 行为了。 notice_ls() notice_ww() } } // 通知 张三 function notice_zs(){ console.log('张三,来报道吧') // 张三收到通知,执行的任务。 } function notice_ls(){ console.log('张三,来报道吧') } function notice_ww(){ console.log('张三,来报道吧') } teacher.start()
说明:这里代码的逻辑是清晰了。问题是 start 函数和 订阅者函数紧紧写在一起。未来需求变动,需要往里面再增加 订阅者就不方便。需要在两个地方同时写代码。
两者 代码分隔太远,关注点 太分离。不易 阅读 和 修改。
3、再次改进下,类似 addEventListener 绑定 事件【注意:观察者模式不是事件的绑定】。不管在哪里绑定事件函数。只要事件触发了,绑定的函数都会触发。
//发布者 let teacher = { customerList:[], dengji(fn){ this.customerList.push(fn) }, start(){ console.log('开学了') this.customerList.forEach(fn => { fn() }) } } teacher.dengji(notice_zs) function notice_zs(){ console.log('张三,来报道') } teacher.dengji(notice_ls) function notice_ls(){ console.log('李四,来报道') } teacher.dengji(notice_ww) function notice_ww(){ console.log('王五,来报道') } // 需求变动,增加功能。直接订阅方法就可以了 teacher.dengji(function (){ console.log('插班生,来报道'); }) teacher.start()
说明:1、现在,后面有新的功能增加,订阅下这个方法就可以。start执行时,就会把订阅的方法全部执行。如果需要,可以再写一个取消订阅的方法。
2、这种模式 解决了 耦合的 问题。各订阅者 和 发布者的代码都没有 耦合关系。可以在 项目中任何地方,订阅者之间没有任何关系。
4、程序再次改进下。每个订阅者,可以根据需求进行订阅,符合订阅者要求的,才会通知(执行函数)他。
//发布者,这里以招生老师来做 例子 let teacher = { customerList:{}, dengji(fn, type){ if(!this.customerList[type]){ this.customerList[type] = [] } this.customerList.push(fn) }, start(type){ console.log(type + '开学了') if(!this.customerList[type]){ return } this.customerList[type].forEach(fn => { fn() }) } } teacher.dengji(notice_zs, 'web') // 登记 的学生,是web班的。web 班 开学,通 web 班对应的学生。其他的 班的学生不通知 function notice_zs(){ console.log('张三,来报道') } teacher.dengji(notice_ls, 'java') function notice_ls(){ console.log('李四,来报道') } teacher.dengji(notice_ww) function notice_ww(){ console.log('王五,来报道') } // 需求变动,增加功能。直接订阅方法就可以了 teacher.dengji(function (){ console.log('插班生,来报道'); }) teacher.start('web') // web 班开学了
5、程序要更复杂一点,发布者 还可以 通知 不同内容(传参)给 订阅者。这里就不写代码,理解上面的 逻辑。自然就知道怎么处理了。
总结:发布者 不用 管 订阅者 都有谁,只要 start 开始,订阅者都会执行。
参考:https://www.bilibili.com/video/BV1Ap4y1y7gS?from=search&seid=1911502725376732686