• 设计模式之发布订阅、观察者模式


    一、观察者模式

    观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新

    观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯

    例如生活中,我们可以用报纸期刊的订阅来形象的说明,当你订阅了一份报纸,每天都会有一份最新的报纸送到你手上,有多少人订阅报纸,报社就会发多少份报纸

    报社和订报纸的客户就形成了一对多的依赖关系

    实现代码如下:

    被观察者模式

    1. class Subject {
    2. constructor() {
    3. this.observerList = [];
    4. }
    5. addObserver(observer) {
    6. this.observerList.push(observer);
    7. }
    8. removeObserver(observer) {
    9. const index = this.observerList.findIndex(o => o.name === observer.name);
    10. this.observerList.splice(index, 1);
    11. }
    12. notifyObservers(message) {
    13. const observers = this.observeList;
    14. observers.forEach(observer => observer.notified(message));
    15. }
    16. }

    观察者:

    1. class Observer {
    2. constructor(name, subject) {
    3. this.name = name;
    4. if (subject) {
    5. subject.addObserver(this);
    6. }
    7. }
    8. notified(message) {
    9. console.log(this.name, 'got message', message);
    10. }
    11. }

    使用代码如下:

    1. const subject = new Subject();
    2. const observerA = new Observer('observerA', subject);
    3. const observerB = new Observer('observerB');
    4. subject.addObserver(observerB);
    5. subject.notifyObservers('Hello from subject');
    6. subject.removeObserver(observerA);
    7. subject.notifyObservers('Hello again');

    上述代码中,观察者主动申请加入被观察者的列表,被观察者主动将观察者加入列表

    二、发布订阅模式

    发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在

    同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者存在

    实现代码如下:

    1. class PubSub {
    2. constructor() {
    3. this.messages = {};
    4. this.listeners = {};
    5. }
    6. // 添加发布者
    7. publish(type, content) {
    8. const existContent = this.messages[type];
    9. if (!existContent) {
    10. this.messages[type] = [];
    11. }
    12. this.messages[type].push(content);
    13. }
    14. // 添加订阅者
    15. subscribe(type, cb) {
    16. const existListener = this.listeners[type];
    17. if (!existListener) {
    18. this.listeners[type] = [];
    19. }
    20. this.listeners[type].push(cb);
    21. }
    22. // 通知
    23. notify(type) {
    24. const messages = this.messages[type];
    25. const subscribers = this.listeners[type] || [];
    26. subscribers.forEach((cb, index) => cb(messages[index]));
    27. }
    28. }

    发布者代码如下:

    1. class Publisher {
    2. constructor(name, context) {
    3. this.name = name;
    4. this.context = context;
    5. }
    6. publish(type, content) {
    7. this.context.publish(type, content);
    8. }
    9. }

    订阅者代码如下:

    1. class Subscriber {
    2. constructor(name, context) {
    3. this.name = name;
    4. this.context = context;
    5. }
    6. subscribe(type, cb) {
    7. this.context.subscribe(type, cb);
    8. }
    9. }

    使用代码如下:

    1. const TYPE_A = 'music';
    2. const TYPE_B = 'movie';
    3. const TYPE_C = 'novel';
    4. const pubsub = new PubSub();
    5. const publisherA = new Publisher('publisherA', pubsub);
    6. publisherA.publish(TYPE_A, 'we are young');
    7. publisherA.publish(TYPE_B, 'the silicon valley');
    8. const publisherB = new Publisher('publisherB', pubsub);
    9. publisherB.publish(TYPE_A, 'stronger');
    10. const publisherC = new Publisher('publisherC', pubsub);
    11. publisherC.publish(TYPE_C, 'a brief history of time');
    12. const subscriberA = new Subscriber('subscriberA', pubsub);
    13. subscriberA.subscribe(TYPE_A, res => {
    14. console.log('subscriberA received', res)
    15. });
    16. const subscriberB = new Subscriber('subscriberB', pubsub);
    17. subscriberB.subscribe(TYPE_C, res => {
    18. console.log('subscriberB received', res)
    19. });
    20. const subscriberC = new Subscriber('subscriberC', pubsub);
    21. subscriberC.subscribe(TYPE_B, res => {
    22. console.log('subscriberC received', res)
    23. });
    24. pubsub.notify(TYPE_A);
    25. pubsub.notify(TYPE_B);
    26. pubsub.notify(TYPE_C);

    上述代码,发布者和订阅者需要通过发布订阅中心进行关联,发布者的发布动作和订阅者的订阅动作相互独立,无需关注对方,消息派发由发布订阅中心负责

    三、区别

    两种设计模式思路是一样的,举个生活例子:

    • 观察者模式:某公司给自己员工发月饼发粽子,是由公司的行政部门发送的,这件事不适合交给第三方,原因是“公司”和“员工”是一个整体
    • 发布-订阅模式:某公司要给其他人发各种快递,因为“公司”和“其他人”是独立的,其唯一的桥梁是“快递”,所以这件事适合交给第三方快递公司解决

    上述过程中,如果公司自己去管理快递的配送,那公司就会变成一个快递公司,业务繁杂难以管理,影响公司自身的主营业务,因此使用何种模式需要考虑什么情况两者是需要耦合的

    两者区别如下图:

    • 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。

    • 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。

    • 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)

  • 相关阅读:
    题目 1069: 二级C语言-寻找矩阵最值
    搜索技术——盲目与启发
    前端三刺客---CSS
    金融工程学学习笔记第一章
    SAP 选择屏幕动态通过Radio Button 显示与隐藏以及控制是否必输
    C#,入门教程——关于函数参数ref的一点知识与源程序
    JPA-Specification常用条件查询构造方式
    软考-访问控制技术原理与应用
    Python中series.unique()返回序列中所有不重复的元素。
    基于 Flink CDC 实现海量数据的实时同步和转换
  • 原文地址:https://blog.csdn.net/Ming_xm/article/details/134326343