一次搞定观察者模式与订阅发布模式

定义:

观察者模式(observer pattern)是一种行为设计模式,在多个对象中定义一种一对多的依赖,这样当一个对象状态发生改变时,可以通知到该对象状态的依赖方并更新。

问题&解决方案

问题:

假设我们现在要在前端页面绘制饼图,柱状图和线图这三个对象,绘图所需要的数据都在一个主题对象中,这些需要被绘制的图像与主题对象之间是没有联系的,而主题对象发生改变时,我们需要重新绘制表格图。 我们可以在这些图中的代码写一个轮询,每当30ms就查询主题对象是否发生改变,如果改变则进行重新绘制,这无疑是对资源的一种浪费,因为轮询会一直进行但数据可能在生命周期内都不会发生改变。我们也可以让主题对象进行通知,每次发生改变时,就通知到这三种图像组件代码,而由于其中依赖数据存在不同,可能会导致有些通知是不必要的。这时我们考虑使用观察者模式,建立起主题与被观察者之间的关系并在合适的时机去更新对应的图。

解决方案:

通过上面的问题描述,我们要做的是建立主题对象与观察者之间的关系,在数据发生改变时,通知到对应的观察者并更新。将依赖数据和观察者对象代码解耦, 同时提高了复用与可维护性。

涉及到的Object:

通常观察者模式会涉及到两个对象:

  • Subject (主题): 用来维护多个依赖对象,提供新增和删除某个依赖对象并且当状态发生改变时,通知依赖对象。

    typescript 复制代码
    interface Subject {
      subscribe: Function,
      unSubscribe: Function,
      Notify: Function
    }
  • Observer(观察者): 表示对某些状态有依赖的对象。

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

引用《设计模式》这本书中在介绍 observer pattern的一句话, This kind of interaction is also known as publish-subscribe ,这句话翻译过来也就是这种两者之间关系也被称为发布者-订阅者。笔者认为,我们在面试中被问到这两个模式(订阅发布模式和观察者模式),其实本质上都是用来建立起主体与观察者之间的关系,处理两者通信的设计模式,而两者在具体实现上有着本质的区别。接下来就让我们一起来看一下二者的区别与具体实现:

观察者模式(Observer Pattern):

实现一个观察者模式并不困难,网上也有很多例子, 我们首先实现一个Subject用来添加,删除和通知观察者:

js 复制代码
class Subject {
  constructor() {
    this.state = null; // 数据
    this.observers = [];
  }
  
  subscribe(func) {
    this.observers.push(func);
  }
  
  unSubscribe(func) {
    this.observers.filter(updateFunc => updateFunc !== func);
  }
  
  notify() {
        for (const ob of this.observers) {
            ob(this.state);
        }
  }
​
    setState(newState) {
        this.state = newState;
    }
}
​
const observableSubject = new Subject();
​
const observerOne = function(data) {
    console.log(`current data is: ${data}`);
}
​
const observerTwo = function(data) {
    console.log(`Date is ${Date.now()}, the data is ${data}`)
}
​
observableSubject.subscribe(observerOne);
observableSubject.subscribe(observerTwo);
​
observableSubject.setState(1); // 设置数据为 1
​
observableSubject.notify(); // 通知观察者
​
observableSubject.unSubscribe(observerTwo);
observableSubject.notify();
  1. 主题-观察者的关系:存在一个主题(Subject)和多个观察者(Observer)。主题维护一组观察者,提供新增和删除某个依赖对象并且当状态发生改变时,通知观察者。
  2. 状态:在观察者模式中,观察者主动注册,在状态发生改变时,主动接收通知
  3. 耦合程度:观察者通常会直接调用主题的方法来注册或取消注册,主题与观察者之间存在一定的耦合。
订阅发布模式 (Publish-Subscribe Pattern):

在实现原理上,订阅发布模式在会新增一个事件通道作为调度中心,管理事件的订阅与发布,解耦了上述主题和观察者的耦合关系。

js 复制代码
class PubSub {
    constructor() {
        this.events = {};
    }
​
    subscribe(topic, callback) {
        if (!this.events[topic]) {
            this.events[topic] = [];
        }
        this.events[topic].push(callback);
    }
​
    unSubscribe(topic, callback) {
        if (!this.events[topic]) {
            console.error('topic does not exist');
        }
​
        this.events[topic] = this.events[topic].filter((cb) => cb !== callback);
    }
​
    publish(topic, data) {
        if (this.events[topic]) {
            this.events[topic].forEach((callback => callback(data)));
        }
    }
}
​
const pubsub = new PubSub();
pubsub.subscribe('email/Login', (email) => {
    console.log("Subscriber 1, email login: ", email)
});
​
pubsub.subscribe('email/Login', (email) => {
    console.log("Subscriber 2, email login: ", email)
});
​
function loginEmail(email) {
    pubsub.publish('email/Login', email);
}
​
loginEmail('myEmail@123.com');
  1. 发布者-订阅者关系:在订阅发布模式中,有一个中心的事件调度器,用来当发布者,其他对象则是订阅者。发布者和订阅者之间是一对多的。
  2. 事件的中心化管理 :事件的发布和订阅是中心化管理的,发布者将事件发送到事件调度器,然后由事件调度器将事件广播给所有订阅者, 详细可见 publish 函数。
  3. 松散耦合:对比观察者模式,订阅发布模式会减少对象之间的依赖性,使系统中的各个部分相对独立,从而实现了松散耦合。

区别

  1. 中心化 vs. 分散化

    • 订阅发布模式是中心化的,事件的发布和订阅由一个事件调度器管理。
    • 观察者模式是分散化的,每个观察者独立注册和接收通知,主题不直接参与通知的传递。
  2. 耦合程度

    • 订阅发布模式通常实现了松散耦合,发布者和订阅者之间的依赖性较低。
    • 观察者需要了解主题的接口,并注册和取消注册,所以观察者模式主题与观察者对象间存在较高的耦合。

选择使用哪种模式取决于项目的需求和设计目标。订阅发布模式适用于需要将事件中心化管理的情况,而观察者模式适用于需要将通知分散到多个观察者的情况。

last words

Redux : 其实redux在核心(redux-core)也有 subcribe 函数通过观察者模式来订阅每次store 修改的情况, 具体应用价值则可以参考 react-redux 中的 connect 函数。这里需要主要的是,Redux的store更新并不是观察者模式方式而是单向数据流修改,希望大家不要弄混两个概念。

希望本文能对你学习有帮助,如果你认为有用的话,也麻烦一键三连,有任何问题也希望能够沟通讨论~

相关推荐
编程、小哥哥2 分钟前
设计模式之抽象工厂模式(替换Redis双集群升级,代理类抽象场景)
redis·设计模式·抽象工厂模式
web Rookie29 分钟前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
Au_ust37 分钟前
css:基础
前端·css
帅帅哥的兜兜37 分钟前
css基础:底部固定,导航栏浮动在顶部
前端·css·css3
yi碗汤园40 分钟前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
就是个名称41 分钟前
购物车-多元素组合动画css
前端·css
编程一生1 小时前
回调数据丢了?
运维·服务器·前端
丶21361 小时前
【鉴权】深入了解 Cookie:Web 开发中的客户端存储小数据
前端·安全·web
Missmiaomiao2 小时前
npm install慢
前端·npm·node.js
程序员爱技术4 小时前
Vue 2 + JavaScript + vue-count-to 集成案例
前端·javascript·vue.js