观察者模式VS发布订阅模式

导读:本篇文章讲解 观察者模式VS发布订阅模式,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

一、前言

观察者模式vs发布/订阅模式很容易混淆,像是凤梨和菠萝,傻傻分不清。Observer模式通常用Publish/Subscribe模式实现,我刚开始学习js的时候,以为这是同一回事,但是仔细学习,发现它们是有质的区别的。

二、观察者模式vs发布/订阅模式

1、观察者模式

在这里插入图片描述

2、发布/订阅模式

在这里插入图片描述

三、观察者模式

3.1 什么是观察者模式

官方:定义对象间的一对多的依赖关系,当一个对象发生改变时,所有依赖于他的对象都会受到通知。

通俗:一个对象(subject)维持一系列依赖它的对象(observer),将有关状态的任何变更自动通知它们(observer)

3.2 举个栗子

春招我喜欢A公司,于此同时1同学2同学3同学都喜欢,我们都在A公司的HR那留下简历,HR说一旦前端开发开始招聘就通知我和1同学2同学3同学。这里我和1同学2同学3同学(Observers)依赖A公司(Subject)的HR,一旦前端开发开始招聘(Event happen),HR就会通知(notify)我们(Observers)

在这里插入图片描述

3.3 两种主体,目标对象(Object)和观察者(Observer)

(1)目标对象Object

①维护observerList列表

②定义添加observer的方法

③当object自身发生变化,调用自己的notify()方法通知每个观察者执行 update() 方法

(2)观察者对象Observer

①update方法执行自定义的事件

②实现update供object对象调用

3.4 代码实现

简单版本的代码实现

    class Observer {
        /**
         *构造器
         * @param{Function} cb 回调函数,收到目标对象通知时执行
         **/
        constructor(cb) {
            if (typeof cb === 'function') { //检测是否是函数类型
                this.cb = cb
            } else { //不是函数类型抛出异常
                throw new Error('Observer构造器必须传入的是函数类型')
            }
        }
        /*被目标对象object 通知notify时执行*/
        update() {
            this.cb()
        }
    }

    //目标对象
    class Subject {
        constructor() {
            //维护观察者表列 
            this.observerList = []
        }
        /**
         * @param {Observer} observer Observer实例
         **/
        addObserver(observer) {
            this.observerList.push(observer)
        }
        /**
         * 通知所有观察者observer
         **/
        notify() {
            this.observerList.forEach(observer => {
                observer.update()
            })
        }
    }

    const observerCallback = function () {
        console.log('I am be notified')
    }
    const observer = new Observer(observerCallback)

    const subject = new Subject();
    subject.addObserver(observer);
    subject.notify();
3.5 观察者模式的特点

①角色分工明确,没有事件调度中心作为中间者。目标对象Subject和观察者Observer实现约定

②目标对象Subject和观察者Observer联系紧密。

四、发布/订阅模式

3.1 什么是发布/订阅模式

基于一个事件(主题)通道,希望接收通知的对象 Subscriber 通过自定义事件订阅主题,被激活事件的对象 Publisher 通过发布主题事件的方式通知各个订阅主题的 Subscriber。

3.2 举个栗子

女生喜欢追剧,迫不及待的等《甄嬛传》更新,想在第一时间开始追最新集的《甄嬛传》,那么我该怎么办呢?我总不是抱着平板时时刻刻刷新吧。某个视频平台提供了订阅的功能,《甄嬛传》更新后,会第一时间通知你。

在这里插入图片描述

3.3 三个角色 发布者Publisher、事件调度中心 Event Channel、订阅者Subscriber

(1)任务发布者 —— Publisher

(2)事件调度中心——中介 Event Channel

①维护任务类型,以及每种情况下的订阅情况

②给订阅者提供订阅功能 subscribe

③给订阅者发布任务 publish

(3)订阅者(任务接受者)—Subscriber

3.4 代码实现
class PubSub {
    constructor() {
        // 维护事件及订阅行为
        this.events = {}
    }
    /**
     * 注册事件订阅行为
     * @param {String} type 事件类型
     * @param {Function} cb 回调函数
     */
    subscribe(type, cb) {
        if (!this.events[type]) {
            this.events[type] = []
        }
        this.events[type].push(cb)
    }
    /**
     * 发布事件
     * @param {String} type 事件类型
     * @param  {...any} args 参数列表
     */
    publish(type, ...args) {
        if (this.events[type]) {
            this.events[type].forEach(cb => {
                cb(...args)
            })
        }
    }
    /**
     * 移除某个事件的一个订阅行为
     * @param {String} type 事件类型
     * @param {Function} cb 回调函数
     */
    unsubscribe(type, cb) {
        if (this.events[type]) {
            const targetIndex = this.events[type].findIndex(item => item === cb)
            if (targetIndex !== -1) {
                this.events[type].splice(targetIndex, 1)
            }
            if (this.events[type].length === 0) {
                delete this.events[type]
            }
        }
    }
    /**
     * 移除某个事件的所有订阅行为
     * @param {String} type 事件类型
     */
    unsubscribeAll(type) {
        if (this.events[type]) {
            delete this.events[type]
        }
    }
}
3.5 发布/订阅模式的特点

①发布者Publisher和订阅者Subscriber相互匿名,没有特殊的约束。通过事件调度中心Event Channel提供的接口发布和订阅事件

②松散耦合,灵活度高

③但事件越来越多的时候,会难以维护,需要考虑事件命名规范和避免数据流混乱

④用来处理不同系统组件的信息交流,也可以是非父子组件,类似vuex

五 观察者模式和发布/订阅模式的异同简单总结

设计模式 观察者模式 发布/订阅模式
主体 Object观察者、Subject目标对象 Publisher发布者、Event Channel事件调度中心、Subscriber订阅者
主体关系 Subject中通过observerList记录ObServer Publisher和Subscribe互相匿名,通过事件调度中心联系
优点 Subject和Object联系紧密,遵循规则方法 松散耦合,灵活度高,异步编程
缺点 紧耦合 当事件类型变多时,会增加维护成本
应用 数据双向绑定 事件总线EventBus

(1)观察者模式中Object观察者和Subject目标对象互相知道对方;发布/订阅模式互相匿名,通过事件调度中心Event Channel中介进行信息通信代理。

(2)观察者模式组件时紧耦合;发布/订阅模式组件是松散耦合

(3)观察者模式大多数时候是同步的,事件发布/事件触发的时候,Subject就会去调用观察者模式;发布/订阅模式大多数是异步的,Publisher发布到消息通信代理中心,Subscriber去消息代理中心获取消息。

(4)观察者模式定义一对多的依赖关系,要求Observer必须subscribe订阅改变内容的事件

(5)观察者模式需要目标对象objectt和观察者observer关联在一起;发布/订阅模式需要借助管道,使用一个主题/事件通道,介于Publisher和Subscriber之间。

(6)观察者模式的Observer被动执行内容改变事件;发布/订阅模式的Subscriber可以自定义事件处理程序。

六、观察者模式应用和发布/订阅模式应用

子组件和父组件之间的通信,Vue中通过props完成父组件向子组件传递数据比如$ emit用来发布消息并且对

$on做统一处理。实现非父子组件通信,Vue中通过vuex,EventEmitter2

(1)DOM绑定事件—发布/订阅模式
    document.querySelector('#btn').addEventListener('click',function () {
        alert('You click this btn!Hello World!');
    },false)

监听用户点击按钮的动作,但是不知道什么时候用户会点击按钮,所以需要去订阅click事件。

(2)数据双向绑定—观察者模式

在这里插入图片描述

双向绑定实现原理:数据劫持Object.defineProperty()、监听器Observer监听所有的属性,若发生变化告诉Watcher订阅者更新数据,解析器Compile解析对应的指令,执行更新的函数从而更新视图,实现双向绑定。

七、总结

在项目中,这两种模式有效果就好。这两种模式高度相似,没有必要强行区分理解他们。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/16135.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!