javascript设计模式学习之八_发布订阅,观察者模式

一、发布订阅模式定义

jQuery中的callbacks,defered,promise本质上就是发布订阅模式的实现。ES6的promise内部实现未开源,不了解具体机制

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

发布—订阅模式的优点十分明显,一是可以实现时间上的解耦,二是可以实现对象之间的解耦。

发布—订阅模式的缺点也很明显,订阅者订阅一个消息后,如果该消息最后都没有发生,这个订阅者依旧会存在于内存中。

javascript中的事件机制就属于发布订阅模式的一种。

如document.body.addEventListener(),这里document.body就是事件的发布者,addEventListener就是事件发布者提供的一个订阅事件的接口方法,所有其他对象都可以调用这个方法来订阅document.body上发生的对应事件,并自定义自己的事件处理方法。

一般的发布订阅者模式中,发布者还会提供一个trigger方法,用于发布消息。

二、java中观察者模式实现

可以先看看编译型语言如java中观察者模式的实现,参见博客:

http://www.cnblogs.com/weijunqiang/p/3113828.html

在java中实现一个自己的发布—订阅模式,通常会把订阅者对象自身当作引用传入发布者对象中,同时订阅者还会提供一个诸如update(本例中是seeMail)的方法,供发布者在合适的时候调用。在Javascript中,可以使用注册回调函数的方式来代替传统的发布—订阅模式,更加简单和优雅。

三、一个通用的发布-订阅模式

    var events=(function(){
        var clientList={};
        var listen=function(key,fn){
            if(!clientList[key]){
                clientList[key]=[];
            }
            clientList[key].push(fn);
        };
        var trigger=function(){
            var key=[].shift.apply(arguments);
            var fns=clientList[key];
            if(!fns||fns.length==0){
                return false;
            }
            for(var i=0;i<fns.length;i++){
                fns[i].apply(this,arguments);
            }
        };
        var remove=function(key,fn){
            var fns=clientList[key];
            if(!fns){
                return false;
            }
            if(!fn){
                fns.length=0;
                return;
            }
            for(var i=0;i<fns.length;i++){
                if(fn===fns[i]){
                    fns.splice(i,1);
                }
            }
        };
        return{
            listen:listen,
            trigger:trigger,
            remove:remove,
    };
    })();
    
    var installEvent=function(obj){
        for(var i in events){
            obj[i]=events[i];
        }
    };//该函数可以给所有对象添加动态发布功能

四、发布——订阅模式示例(网站登录

    //现在需要实现网上商城,网站有header头部,nav导航,消息列表,购物车等,
    //这些模块有一个共同的特征,需要ajax获取到用户信息之后才能进行渲染,如header显示用户头像等等
    
    
    //1,实现login对象,作为发布者
    var login={};
    installEvent(login);
    $.ajax('http://www.bobo.com',function(data){
        login.trigger('loginSucc',data);
    });
    
    //各个模块监听登陆成功的消息
    var header=(function(){
        login.listen('loginSucc',function(data){
            header.setAvatar();
        });//监听登陆成功时间
        return {
            setAvatar:function(data){
                    console.log('设置header模块');
            }
        };
    })();
    var nav=(function(){
        login.listen('loginSucc',function(data){
            nav.setAvatar();
        });
        return {
            setAvatar:function(data){
                console.log('设置nav模块');
            }
        };
    }();