js设计模式之观察者模式(发布-订阅模式)

前言 现实中的发布-订阅模式

  小明最近看上了一套房子,到了售楼处之后才被告知,该楼盘的房子早已售罄。好在售楼MM告诉小明,不久后还有一些尾盘推出,开发商正在办理相关手续,手续办好后就可以购买。但到底什么时候,目前还没有人知道。
  于是小明记下了售楼处的电话,以后每天都会打电话过去询问是不是到了购买时间。除了小明,还有小红、小强、小龙也会每天向售楼处咨询这个问题。一个星期后,售楼MM决定辞职,因为厌倦了每天回答1000个相同内容的电话。
  当然现实中没有这么笨的售楼公司,实际上故事是这样的:小明离开之前,把电话号码留在了售楼处。售楼MM答应他,新楼盘一推出就马上发信息通知小明。小红、小强、小龙也是一样,他们的电话号码都被记在售楼处的花名册上,新楼盘推出的时候,售楼MM会翻开花名册,遍历上面的电话号码,依次发送一条短信来通知他们。

一、最简单的例子

// 订阅信息
document.body.addEventListener('click', function(){
    alert(1);
},false);
// 订阅信息
document.body.addEventListener('click', function(){
    alert(2);
},false);

// 发布消息
document.body.click();

document.body订阅了click事件,然后当它被点击(发布)的时候,接收到了信息。

二、发布-订阅模式的模式简单实现

// 定义售楼处
var salesOffices = {};
// 售楼花名册
salesOffices.clientList = [];
// 订阅售楼消息的函数
salesOffices.listen = function (fn) {
    this.clientList.push(fn);
}
// 发布售楼消息的函数
salesOffices.trigger = function () {
    // 遍历花名册,给留了电话号码的客户发送信息
    for (var i = 0, fn; fn = this.clientList[i++];) {
        debugger;
        fn.apply(this, arguments);
    }
}

// 小明订阅售楼信息
salesOffices.listen(function (price) {
    console.log('price=' + price);
});
// 小红订阅售楼信息
salesOffices.listen(function (price) {
    console.log('price=' + price);
});

// 售楼处发布售楼信息
salesOffices.trigger('squareMeter88', 200000);
salesOffices.trigger('squareMeter110', 300000);

简单的发布-订阅模式存在一个缺陷:就是如果小明只想订阅88平米的楼盘,但是售楼处会把所有的楼盘信息都推送给了他。我们可以做得更智能一些,给订阅的函数多传一个参数,表明订阅者只订阅那一个消息。

三、发布-订阅模式的模式进阶实现

// 定义售楼处
var salesOffices = {};
// 定义客户花名册
salesOffices.clientList = [];
// 定义订阅方法
salesOffices.listen = function (key, fn) {
    // 花名册登记哪些客户订阅哪些信息(根据key来区分)
    if (!this.clientList[key]) {
        this.clientList[key] = [];
    }
    this.clientList[key].push(fn);
}
salesOffices.trigger = function () {
    // 获取key,获取第一个参数
    var key = Array.prototype.shift.call(arguments);
    var fns = this.clientList[key];
    if (!fns || fns.length === 0) {
        return false;
    }
    for (var i = 0, fn; fn = fns[i++];) {
        fn.apply(this, arguments);
    }
}

// 小明定于88平米的楼盘
salesOffices.listen('squareMeter88', function (price) {
    console.log('price=' + price);
});
// 小红定于100平米的楼盘
salesOffices.listen('squareMeter100', function (price) {
    console.log('price=' + price);
});

// 售楼处发布楼盘信息
salesOffices.trigger('squareMeter88', 2000000);
salesOffices.trigger('squareMeter100', 3000000);

四、封装订阅-发布模式

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);
        var fns = this.clientList[key];
        if (!fns || fns.length === 0) {
            return false;
        }
        for (var i=0,fn;fn=fns[i++];) {
            fn.apply(this, arguments);
        }
    }
}

function installEvent(obj) {
    for (var i in event) {
        obj[i] = event[i];
    }
}

var salesOffices = {};
installEvent(salesOffices);

salesOffices.listen('squareMeter88', function(price) {
    console.log(`price=${price}`);  
});
salesOffices.trigger('squareMeter88',200000);
上一篇:开发指南专题八:JEECG微云高速开发平台数据字典


下一篇:hint bits 源码讲解, 为什么PostgreSQL查询语句也可能产生 xlog, 并且可能对buffer有write操作 ?