设计模式1—发布订阅者模式【行为型】

设计模式的目的是为了改善代码

一、概念:

  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

原文地址:https://www.cnblogs.com/wfblog/p/14347464.html