聊一聊发布订阅设计模式

背景:在某一期需求(小程序)开发中,遇到了一小问题:两个不同的模块需要通信,完成一个事件交互。我想了半天:这个地方也没有像ng与vue里的事件派发机制,该怎么通知完成交互呢?最后还是没想出来,于是去请教了一起开发这个需求的同组大神:

我:大神,这个地方该怎么处理呢?
大神:这很简单,我们可以用观察者模式来解决。
我(一脸懵逼):啊,观察者模式是啥?(内心戏:啊,mmp,这是啥子哦,不懂撒)。
大神:嗯,没事,这块待会我来写,你可以先写其他的内容。
我:哦哦,好的。(内心戏:能解决就好,不过这个到底是个啥子东西哦)
复制代码

当时内心是崩溃的,借用电子竞技里很有名的一句话:菜是原罪。于是便有了今天的这篇分享,弥补下薄弱的基础0.0

一,什么是发布订阅模式

1,官方释义

发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象
同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
复制代码

2,现实中的发布订阅

小红最近在淘宝网上看上一双鞋子,但是呢 联系到卖家后,才发现这双鞋卖光了,但是小红对这
双鞋又非常喜欢,所以呢联系卖家,问卖家什么时候有货,卖家告诉她,要等一个星期后才有货,
卖家告诉小红,要是你喜欢的话,你可以收藏我们的店铺,等有货的时候再通知你,所以小红收
藏了此店铺,但与此同时,小明,小花等也喜欢这双鞋,也收藏了该店铺;等来货的时候就依次会
通知他们;
复制代码

在上面的故事中,卖家是属于发布者,小红,小明等属于订阅者,订阅该店铺,卖家作为发布者,当鞋子到了的时候,会依次通知小明,小红等,依次使用旺旺等工具给他们发布消息。

二,实现发布订阅

1,简易的发布订阅

上面对发布订阅模式做了个简单的介绍,现在让我们来撸个简易版的发布订阅代码。

// 自定义一个事件管理对象
var evnetManager = {};

// 事件管理列表
eventManager.list = {};

// 监听事件
eventManager.subscribe = function (key, fn) {
    // 判断该事件是否被监听
    if (!this.list[key]) {
        this.list[key] = [];
    }
    this.list[key].push(fn);
}

// 发布事件
eventManager.publish = function () {
    // 利用arguments取参数
    let key = [].shift.call(arguments), fns = this.list[key];
    if (!fns || fns.length == 0) {
        return false;
    }
    // 依次调用相关函数
    fns.forEach((fn) => {
        fn.apply(this, arguments);
    })
}
复制代码

好了,上面是一个简易的发布订阅,具备监听与发布,现在让我们来验证下正确与否:

eventManager.subscribe('fire', function (person) {
    console.log(person + '订阅了fire事件');
});

eventManager.publish('fire', '小红');
eventManager.publish('fire', '小明');

// 小红订阅了fire事件
// 小明订阅了fire事件
复制代码

nice,这样一个简易版的就实现了,但是不够通用化,让我们来写个标准版的。

2,用class类实现发布订阅

不多说,直接上菜:

// 声明事件管理类
class EventManager {
    constructor () {
        this.subscriberMap = {};
    }
    
    /**
     * 订阅事件
     * @param {*} hash 
     * @param {*} subscriber 
     */
     subscribe(hash, subscriber) {
        var arr = this.subscriberMap[hash];
        if (!arr) {
            arr = this.subscriberMap[hash] = [];
        }
        // 判断事件对应的回调函数是否存在,防止重复添加。
        if (!arr.includes(subscriber)) {
            arr.push(subscriber);
        }
    }
    
    /**
     * 发布事件
     * @returns {boolean}
     */
    publish() {
        let hash = [].shift.call(arguments);
        let fns = this.subscriberMap[hash];
        if (!fns || fns.length == 0) {
            return false;
        }
        fns.forEach((fn) => {
            fn.apply(this,arguments);
        })
    }
    
    /**
     * 移除某个事件的订阅
     * @param hash {String}
     * @param subscriber {ReceiveEventAgent}
     */
    remove(hash, subscriber) {
        var arr = this.subscriberMap[hash];
        if (!arr) {
            return false;
        }
        var index = arr.indexOf(subscriber);
        if (index > -1) {
            arr.splice(index, 1);
        }
    }
}
复制代码

好,现在我们已经声明了一个事件管理类,让我们来测试下:

function print(person) {
    console.log(person + '订阅了事件');
}

var event = new EventManager();
event.subscribe('fire', print);

event.publish('fire', '小明');  // 小明订阅了事件

// 取消订阅(这里只是取消了print回调,并不是取消所有)
event.remove('fire', print);
复制代码

好,到此上面就是一个简易通用的发布订阅,可能功能还有待完善,但是应该可以满足一般的业务需求。

三,总结

优点:

  • 时间上的解耦
  • 对象之间的解耦

缺点:

  • 消耗一定的时间和内存
  • 使用过度,会导致程序难以跟踪维护和理解

这个时候让我们来仔细的回顾下,发现发布订阅模式的实现方式就是利用回调,每一个事件key数组,里面存储维护着多个不同的回调函数,当发布事件时,循环调用函数,从而实现发布订阅的效果,关键就是如何管理与维护。当然这些是我自己的一些理解,发布订阅实现的还不仅仅如此,学习的道路还很长,fighting !!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值