zoukankan      html  css  js  c++  java
  • 发布-订阅模式

    发布-订阅模式     

           发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。JavaScript开发中我们一般用事件模型来代替传统的发布-订阅模式

    现实中的发布-订阅模式

          小明最近喜欢上吃老北京烧饼,可是到了卖烧饼的地方发现已经卖完了,而且排队的人还很多.幸运的是卖烧饼那个MM看小明长得帅,告诉小明等一会就有烧饼吃啦!可是小明现在还有约会要去,不知道烧饼能什么时候出锅,总不能因为吃烧饼而不去约会吧!这时候小明灵机一动,说烧饼MM把你电话给我吧!我先去忙,等会打电话问你烧饼好了没有。烧饼MM也没想太多,把电话给小明了。后来小龙也来买烧饼,情况跟小明差不多,小龙也把烧饼MM的电话要走了。可是问题就这来了,小明、小龙一会儿打一个电话给烧饼MM,导致烧饼MM很烦,辞职走了不干了。
    通过上边的事情我们可以发现,存在好多问题
       第一:卖烧饼的MM应该充当发布者
       第二:小明小龙的电话应该保存在卖烧饼的用户列表中,如果卖烧饼的MM离职,这用户就会丢失
       第三:实际上没有这么笨蛋的销售方式的

    卖烧饼的店主可以把小明、小龙的电话记录下来,等店里有烧饼了在通知小龙小明来拿这就是所谓的发布-订阅模式,代码如下:

    /*烧饼店*/        
    var Sesamecakeshop={
        clienlist:[],//缓存列表
        addlisten:function(fn){//增加订阅者
            this.clienlist.push(fn);
        },
        trigger:function(){//发布消息
            for(var i=0,fn;fn=this.clienlist[i++];){
                fn.apply(this,arguments);
            }
        }
    }
    
    /*小明发布订阅*/
    Sesamecakeshop.addlisten(function(price,taste){
        console.log("小明发布的"+price+"元,"+taste+"味道的");
    });
    /*小龙发布订阅*/
    Sesamecakeshop.addlisten(function(price,taste){
        console.log("小龙发布的"+price+"元,"+taste+"味道的");
    });        
    
    Sesamecakeshop.trigger(10,"椒盐");

           从代码中可以看出,只有小明,小龙预定了烧饼,烧饼店就可以发布消息告诉小龙与小明。但是有个问题不知道大家发现了没有。小明只喜欢椒盐味道的。而小龙只喜欢焦糖味道的。上面的代码就满足不了客户的需求,给客户一种感觉就是,不管我喜欢不喜欢,你都会发给我。如果发布比较多,客户就会感到厌烦,甚至会想删除订阅。下边是对代码进行改良大家可以看看。

                
    /*烧饼店*/        
    var Sesamecakeshop={
        clienlist:{},/*缓存列表*/
        /**
         * 增加订阅者
         * @key {String} 类型
         * @fn {Function} 回掉函数
         * */
        addlisten:function(key,fn){
            if(!this.clienlist[key]){
                this.clienlist[key]=[];
            }
            this.clienlist[key].push(fn);
        },
        /**
         * 发布消息
         * */
        trigger:function(){
            var key=[].shift.call(arguments),//取出消息类型
                fns=this.clienlist[key];//取出该类型的对应的消息集合
            if(!fns || fns.length===0){
                return false;
            }
            for(var i=0,fn;fn=fns[i++];){
                fn.apply(this,arguments);
            }
        },
        /**
         * 删除订阅
         * @key {String} 类型
         * @fn {Function} 回掉函数
         * */
        remove:function(key,fn){
            var fns=this.clienlist[key];//取出该类型的对应的消息集合
            if(!fns){//如果对应的key没有订阅直接返回
                return false;
            }
            if(!fn){//如果没有传入具体的回掉,则表示需要取消所有订阅
                fns && (fns.length=0);
            }else{
                for(var l=fns.length-1;l>=0;l--){//遍历回掉函数列表
                    if(fn===fns[l]){
                        fns.splice(l,1);//删除订阅者的回掉
                    }
                }
            }
        }
    }
    
    /*小明发布订阅*/
    Sesamecakeshop.addlisten("焦糖",fn1=function(price,taste){
        console.log("小明发布的"+price+"元,"+taste+"味道的");
    });
    /*小龙发布订阅*/
    Sesamecakeshop.addlisten("椒盐",function(price,taste){
        console.log("小龙发布的"+price+"元,"+taste+"味道的");
    });        
    
    Sesamecakeshop.trigger("椒盐",10,"椒盐");
    
    Sesamecakeshop.remove("焦糖",fn1);//注意这里是按照地址引用的。如果传入匿名函数则删除不了        
                
    Sesamecakeshop.trigger("焦糖",40,"焦糖");    

           删除的时候需要注意的是,如果订阅的时候传递的是匿名函数,删除的时候如果传入的也是匿名函数。则删除不了。因为删除时候是按照地址引用删除的。传进去的两个匿名函数,对应的地址引用是不同的。

    web前端中使用到的发布-订阅模式

          比如咱们常见的用户身份分别有不同的功能,超级管理员拥有最高权限,可以删除修改任意用户。而普通用户则只能修改自己的账户信息。首先是用户身份验证,验证通过之后对应功能才可以显示。

    //登录发布-订阅模式
    login={
        clienlist:{},/*缓存列表*/
        /**
         * 增加订阅者
         * @key {String} 类型
         * @fn {Function} 回掉函数
         * */
        addlisten:function(key,fn){
            if(!this.clienlist[key]){
                this.clienlist[key]=[];
            }
            this.clienlist[key].push(fn);
        },
        /**
         * 发布消息
         * */
        trigger:function(){
            var key=[].shift.call(arguments),//取出消息类型
                fns=this.clienlist[key];//取出该类型的对应的消息集合
            if(!fns || fns.length===0){
                return false;
            }
            for(var i=0,fn;fn=fns[i++];){
                fn.apply(this,arguments);
            }
        }
    }
    //超级管理员修改所有用户
    var editall=(function(){
        login.addlisten("loginsucc",function(data){
            editall.setview(data);
        });
        return{
            setview:function(data){
                console.log(data);
                console.log("超级管理员修改所有用户");
            }
        }
    })();
    
    //仅仅修改自己
    var editOwn=(function(){
        login.addlisten("loginsucc",function(data){
            editOwn.setview(data);
        });
        return{
            setview:function(data){
                console.log(data);
                console.log("仅仅修改自己");
            }
        }
    })();

     发布-订阅模式简单封装

    var _Event=(function(){
        var clienlist={},
        addlisten,trigger,remove;
        /**
         * 增加订阅者
         * @key {String} 类型
         * @fn {Function} 回掉函数
         * */
        addlisten=function(key,fn){
            if(!clienlist[key]){
                clienlist[key]=[];
            }
            clienlist[key].push(fn);
        };
        /**
         * 发布消息
         * */
        trigger=function(){
            var key=[].shift.call(arguments),//取出消息类型
                fns=clienlist[key];//取出该类型的对应的消息集合
            if(!fns || fns.length===0){
                return false;
            }
            for(var i=0,fn;fn=fns[i++];){
                fn.apply(this,arguments);
            }
        };
        /**
         * 删除订阅
         * @key {String} 类型
         * @fn {Function} 回掉函数
         * */
        remove=function(key,fn){
            var fns=clienlist[key];//取出该类型的对应的消息集合
            if(!fns){//如果对应的key没有订阅直接返回
                return false;
            }
            if(!fn){//如果没有传入具体的回掉,则表示需要取消所有订阅
                fns && (fns.length=0);
            }else{
                for(var l=fns.length-1;l>=0;l--){//遍历回掉函数列表
                    if(fn===fns[l]){
                        fns.splice(l,1);//删除订阅者的回掉
                    }
                }
            }
        };
        return{
            addlisten:addlisten,
            trigger:trigger,
            remove:remove
        }
    })();
    
    
    _Event.addlisten("jianbing",function(d,all){
        console.log("发布的消息来自:"+d+",具体信息:"+all);
    });
    _Event.addlisten("jianbing",function(d,all){
        console.log("发布的消息来自:"+d+",具体信息:"+all);
    })
    _Event.trigger("jianbing","小小坤","前端工程师,擅长JavaScript,喜欢结交更多的前端技术人员,欢迎喜欢技术的你加QQ群:198303871")

    总结:

          发布-订阅模式就是常说的观察者模式,在实际开发中非常有用。它的优点是为时间是解耦,为对象之间解构,它的应用非常广泛,既可以在异步编程中也可以帮助我们完成更松的解耦。发布-订阅模式还可以帮助我们实现设计模式,从架构上来看,无论MVC还是MVVC都少不了发布-订阅模式的参与。然而发布-订阅模式也存在一些缺点,创建订阅本身会消耗一定的时间与内存,也许当你订阅一个消息之后,之后可能就不会发生。发布-订阅模式虽然它弱化了对象与对象之间的关系,但是如果过度使用,对象与对象的必要联系就会被深埋,会导致程序难以跟踪与维护。


  • 相关阅读:
    转-jsonp和jsonpcallback的使用
    转-jQuery jsonp跨域请求
    转-彻底弄懂JS的事件冒泡和事件捕获
    转-打造自己的js类库
    (转)JavaScript: 零基础轻松学闭包(1)
    Myslq 之主键
    Myslq 之空值与非空
    Myslq 之记录查找
    Myslq 之插入记录
    Myslq 之查看数据表
  • 原文地址:https://www.cnblogs.com/xiaoxiaokun/p/7294437.html
Copyright © 2011-2022 走看看