MM:亲爱的,你想我吗? 小明:想,非常想! MM:那…那有多想?
小明:恩……我想你想到右手脱臼! MM:#@!#% 小明:那你想我吗? MM:当然~
小明:多想? MM:我…我…我想你想的都手机进水了~

发布-订阅模式     

     
 发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。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,"焦糖");    

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

后来有一天小明一个人去看电影《爱宠大机密》,刚好坐在他隔壁的MM也是一个人,于是小明就和MM聊了起来。

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")

韦德国际 1

一开始,MM好像对小明印象还不错,两人聊得也挺好。

总结:

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

 

 

这个女生说自己特别喜欢小动物,小明听了很动心,这证明女生心地善良呀。

于是小明问:你最喜欢那些小动物呀?

女生A:路虎、悍马、宝马、捷豹,我都超喜欢的。还有天猫也喜欢的。

小明听了,决定大胆表白:那我是程序猿,是不是也算小动物?你喜欢我吗?

女生A:抱歉,你不算!

第二天小明上班,没精打采的,公司的老程序员听说小明是因为相亲失败,于是指点小明去交友网站找女友试试看。小明就注册了一个账号。

第二天老程序员见到小明问:找到什么合适的女孩没?

小明:找到了网站的一个页面bug。

在相亲网站上混了一阵儿,小明也没有什么收获。于是老程序员又指点小明:要明确策略:深度优先还是广度优先?深度优先就是追一个MM追到底,直到失败然后换
个MM继续追;广度优先就是同时追多个MM,一起发展。小明两个策略都尝试了,还是没找到什么合适的妹子。白白的在相亲网站浪费了不少时间。

后来终于,公司里新招了一个妹子,看起来挺不错的,于是小明决定就追她算了。但不巧的是,公司另一位同事小强也看中了这个妹子。于是小明要和小强竞争。

妹子出了一个题:”去环游世界后再来找我!”

小强立即请假,收拾行李出发。

小明绕MM一圈,然后说:”Hello
world!”结果MM立即被小明感动了。小强就这么输给小明了。现在也不知道周游到了地球的哪个角落了。连老程序员也夸小明机智。

小明说:其实我只是习惯在做任何新事情前,先确定能跑通Hello
world这个句子而已啦。

于是这个MM成了小明的女朋友。两人相处之后,互相觉得挺好的,但是MM家里亲戚朋友特别多,其中不少亲戚,包括MM的父母,对小明并不是很满意。

今年春节,MM决定带小明回老家见一下亲戚朋友。本来以为要花很大的功夫来说服家里人。但节后上班,小明居然宣布女票家里对他很满意,准备今年要结婚了!

这下公司同事都很惊诧,纷纷问小明是用了什么绝招,搞定了女票一大家子人?

小明说:其实也很简单,春节前我编了个小软件,把MM家的七姑妈、八大姨的春运火车票都给解决了……真得感谢12306!

来自:程序师
链接:http://www.techug.com/post/story-of-programmer-xiaoming-date-with-girl.html

Author

发表评论

电子邮件地址不会被公开。 必填项已用*标注