美文网首页
设计模式之发布 — 订阅模式

设计模式之发布 — 订阅模式

作者: zhao_ran | 来源:发表于2021-08-16 22:40 被阅读0次

    发布 — 订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

    DOM 事件

    实际上,只要我们曾经在 DOM节点上面绑定过事件函数,那我们就曾经使用过发布 — 订阅模式,来看看下面这两句简单的代码发生了什么事情···

    document.addEventListener('click',function(){
        alert(1);
    })
    
    document.body.click();
    

    在这里需要监控用户点击document.body的动作,但是我们没办法预知用户将在什么时候点击。所以我们订阅 document.body上的 click 事件,当body节点被点击时,body节点便会向订阅者发布这个消息。这很像购房的例子,购房者不知道房子什么时候开售,于是他在订阅消息后等待售楼处发布消息。

    自定义事件

    除了DOM 事件,我们还会经常实现一些自定义的事件,这种依靠自定义事件完成的发布 —订阅模式可以用于任何JavaScript代码中。
    现在看看如何一步步实现发布 — 订阅模式。
    1.首先要指定好谁充当发布者(比如售楼处);
    2.然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者(售楼处的花名册);
    3.最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数(遍历花名册,挨个发短信)。

    var salesOffices = {}; //售楼部
    
    salesOffices.clientList = {}; // 缓存列表
    
    salesOffices.listen = function(key,fn){
        if(!this.clientList[key]){
            this.clientList[key] = [];
        }
        this.clientList[key].push(fn);
    }
    
    salesOffices.trigger = function(){
        var key = Array.prototype.shift.call(arguments); // 取出消息key
        var fns = this.clientList[key];
    
        if( !fns || fns.length === 0 ){
            return false;
        }
    
        for(var i=0; i<fns.length; i++){
            fns[i].apply(this,arguments);  // arguments是发布消息的的时候带的参数
        }
    }
    
    // 测试
    salesOffices.listen('seqaureMetar88', function(price){
        console.log('seqaureMetar88 价格:' +price);
    })
    
    salesOffices.listen('seqaureMetar88', function(price){
        console.log('zz seqaureMetar88 价格:' +price);
    })
    
    salesOffices.listen('seqaureMetar110', function(price){
        console.log('seqaureMetar110 价格:' +price);
    })
    
    salesOffices.trigger('seqaureMetar88', 20000);
    salesOffices.trigger('seqaureMetar110', 30000);
    
    
    发布-订阅模式的通用实现

    我们先抽出来放到一个对象中

    var event = {
        clientList: {},
        listen: function(key,fn){
            if(!this.clientList[key]){
                this.clientList[key] = [];
            }
            this.clientList[key].push(fn);
        },
        trigger: function(){
            var key = Array.prototype.shift.call(arguments); // 取出消息key
            var fns = this.clientList[key];
            if( !fns || fns.length === 0 ){
                return false;
            }
            for(var i=0; i<fns.length; i++){
                fns[i].apply(this,arguments);  // arguments是发布消息的的时候带的参数
            }
        }
    }
    

    在定义一个installEvent函数,可以给所有函数安装发布-订阅功能(也可以用继承)

    var installEvent = function(obj){
        for(var i in event){
            obj[i] = event[i]
        }
    }
    
    // 测试
    var salesOffices = {};
    installEvent(salesOffices);
    
    salesOffices.listen('seqaureMetar88', function(price){
        console.log('seqaureMetar88 价格:' +price);
    })
    
    salesOffices.listen('seqaureMetar88', function(price){
        console.log('zz seqaureMetar88 价格:' +price);
    })
    
    salesOffices.listen('seqaureMetar110', function(price){
        console.log('seqaureMetar110 价格:' +price);
    })
    
    salesOffices.trigger('seqaureMetar88', 20000);  // seqaureMetar88 价格:20000  seqaureMetar88 价格:20000
    salesOffices.trigger('seqaureMetar110', 30000); // seqaureMetar110 价格:30000
    
    小结

    发布 — 订阅模式的优点非常明显,一为时间上的解耦,二为对象之间的解耦。它的应用非常广泛,既可以用在异步编程中,也可以帮助我们完成更松耦合的代码编写。发布 — 订阅模式还可以用来帮助实现一些别的设计模式,比如中介者模式。 从架构上来看,无论是MVC还是MVVM,都少不了发布 — 订阅模式的参与,而且JavaScript本身也是一门基于事件驱动的语言。

    当然,发布 — 订阅模式也不是完全没有缺点。创建订阅者本身要消耗一定的时间和内存,而且当你订阅一个消息后,也许此消息最后都未发生,但这个订阅者会始终存在于内存中。另外,发布 — 订阅模式虽然可以弱化对象之间的联系,但如果过度使用的话,对象和对象之间的必要联系也将被深埋在背后,会导致程序难以跟踪维护和理解。特别是有多个发布者和订阅者嵌套到一起的时候,要跟踪一个 bug不是件轻松的事情。

    相关文章

      网友评论

          本文标题:设计模式之发布 — 订阅模式

          本文链接:https://www.haomeiwen.com/subject/pyxmpltx.html