古调虽自爱,今人多不谈 —— 漫谈发布订阅模式

发布订阅模式又称观察者模式,定义对象间一种一对多的关系,当对象状态改变时,所有依赖他的对象都将得到通知。实现观察者模式的一般流程

  • 首先指定发布者(代表一的对象)
  • 给发布者添加缓存列表用于存放回调函数以便通知订阅者
  • 发布消息是时发布者遍历缓存列表,依次触发里面存放的的订阅者回到函数

穷人版


let salesOffices = {}; // 售楼处 即 发布者
    salesOffices.clientList = []; // 缓存列表 存放订阅者的回调函数

    salesOffices.listen = (fn) => { // 增加订阅者
        salesOffices.clientList.push(fn); // 订阅的消息加进缓存列表
    }

    salesOffices.trigger = (arguments) => {
        salesOffices.clientList.forEach((fn) => { // arguments 是trigger函数接受的参数
            fn.call(this, arguments)
        })
    }

    salesOffices.listen((price) => {
        console.log('小明的消息' + price)
    })

    salesOffices.listen((price) => {
        console.log('小芳的消息' + price)
    })

    salesOffices.trigger(100)
    salesOffices.trigger(200)

加强版

上面的代码中小明和消防会接收到所有的信息,接下来我们在订阅函数中添加一个标识,标志用户特定的信息。

 /*
    * 假设小明只想接受100的消息
    * 那我们就要在添加订阅的时候加入标识
    * */
    let salesOffices = {}; // 售楼处 即 发布者
    salesOffices.clientList = {}; // 缓存列表 存放订阅者的回调函数

    salesOffices.listen = (key, fn) => { // 增加订阅者
        if (!salesOffices.clientList[key]) {
            salesOffices.clientList[key] = [];
        }
        salesOffices.clientList[key].push(fn); // 订阅的消息加进缓存列表
    }

    salesOffices.trigger = (arg1, ...arg2) => {
        let key = arg1;
        let fns = salesOffices.clientList[key];

        if (!fns || fns.length === 0) { // 没有订阅该消息
            return false;
        }

        fns.forEach((v) => {
            v.call(this, arg2) // arguments 指的是回调函数的
        })
    }

    salesOffices.listen(100,(price) => {
        console.log('小明的消息' + price)
    })

    salesOffices.listen(200,(price) => {
        console.log('小芳的消息' + price)
    })

    salesOffices.trigger(100, 2000) // 发布100
    salesOffices.trigger(200, 3000) // 发布200

设计模式的首要原则便是不变的和可变的分开,所以我们可以将观察者对象提取为一个公共的类。

全能版

    /*
    * 观察者类
    * */
    class Oberver {
        constructor () {
         this.clientList = {};
         this.stack = [];
        }
        listen (key, fn) { // key 事件名
            if (!this.clientList[key]) {
                this.clientList[key] = [];
            }

            this.clientList[key].push(fn);
            this.stack.forEach((v, index) => {
                v(key, index)
            })

        };
        trigger (arg1, ...arg2) {
            let key = arg1;
            let fns = this.clientList[key];

            if (!fns || fns.length === 0) {
                this.stack.push((key, index) => {
                    if (key == arg1) {
                        this.trigger(arg1, arg2);
                        this.stack.splice(index, 1);
                    }
                });
                return false;
            }

            fns.forEach(v=> {
                v.call(this, arg2)
            })
        }
    }
    // 删除订阅
    Oberver.prototype.remove = function (key, fn) {
        var fns = this.clientList[key];

        if (!fns) {
            return false;
        }
        if (!fn) {
            fns && (fns.length = 0); // 没有fn标识删除所有的订阅消息属于key的
        } else {
            fns = fns.filter(v => {
                return v != fn
            })
        }

        return fns;
    }
    
    /*
    * 添加具体的订阅发布
    * */
    var salesOffices = new Oberver();

    salesOffices.listen(100, function (prices) {
        console.log('我是小明我订阅了100的信息' + prices)
    })

    salesOffices.listen(200, function (proces) {
        console.log('我是小芳我订阅了200的信息' + proces)
    })

适用场景

观察者模式适用于一对多的业务需求,比如说很多模块都需要登录信息,或者封装的多个模块之间通信。

优点

时间解耦,对象解耦

缺点

创建订阅者本山需要时间,订阅一个消息可能没有任何用处,但还是在内存中。

posted @ 2018-04-28 17:07  木子青牛  阅读(121)  评论(0编辑  收藏  举报