看了这篇文章,观察者模式和发布/订阅模式还想分不清都难

100 阅读3分钟

1. 观察者模式

想象一下,上学的时候你们班的班主任总是蹭你们不注意偷偷站在后门或者窗边,死死盯着你们,这个场景就是观察者模式。其中两个要素就出来了:

  • 观察者(observer)
  • 被观察者(subject)

老师观察你们,是默默的过程,被观察者是不知道的,除非脑袋后面长了第三只眼睛了。 同时老师观察的动作也是持续的,实时的。 因此observer是主动观察,不需要subject通知的。可以想象这个模式的基本方法是observer(subject) , 最终级是observer(state),因为观察的目的是知道subject的状态。

observer如何实时知道状态呢,当然离不开轮询了。

1.1【单一的观察者模式】

实现了单一的观察者的观察行为。 Subject从1开始计时,到100结束; 而observer监听subject的行为,当数值是10的倍数的时候,输出值。

//这是一个subject,被观察者
class Subject {
  #initCount = null;
  #timer = null;
  constructor(initCount = 0) {
    this.#initCount = initCount;
  }
  run() {
    this.#count();
  }
  //模拟一个状态变化的函数,计数到100截止
  #count = () => {
    this.#timer = setTimeout(() => {
      this.#initCount++;
      clearTimeout(this.#timer);
      this.#timer = null;
      if (this.getState() < 100) {
        this.#count();
      }
    }, 100);
  };
  getState() {
    return this.#initCount;
  }
}

function observer(subject){
    let t = setInterval(() => {
      let state = this.subject.getState();
      if (state % 10 === 0){
        console.log(`state值是${state},是10的${state / 10}倍数`);
      }
      if (state >= 100) {
        //停止监控
        clearInterval(t);
      }
    }, 10);
}

1.2 多维的观察者模式

新增观察者管理器,

  • 可以监听subject的state变化
  • 可以管理多个observer,observer相当于回调函数(state=>handler(state)),当state变化时,调用observer方法。
  • 可以扩充,新增添加observer/删除observer等方法,这个比较简单,不详说。
/**观察者模式
 * 被观察对象(subject)
 * 观察者(observer) 主动观察subject,轮询查其状态
 *
 * observer
 * 可以观察到subject的状态,
 * 可以增加监听事件及回调,当subject状态满足条件时,执行回调函数
 */

class Subject {
  #initCount = null;
  #timer = null;
  constructor(initCount = 0) {
    this.#initCount = initCount;
  }
  run() {
    this.#count();
  }
  //模拟一个状态变化的函数,计数到100截止
  #count = () => {
    this.#timer = setTimeout(() => {
      this.#initCount++;
      clearTimeout(this.#timer);
      this.#timer = null;
      if (this.getState() < 100) {
        this.#count();
      }
    }, 100);
  };
  getState() {
    return this.#initCount;
  }
}

class ObserverManager {
  constructor(subject, observers = []) {
    this.observers = observers;
    this.subject = subject;
    this.watch();
  }
  watch() {
    let t = setInterval(() => {
      let state = this.subject.getState();
      this.notify(state);
      if (state >= 100) {
        //停止监控
        clearInterval(t);
      }
    }, 10);
  }

  notify(state) {
    this.observers.forEach((observer) => observer(state));
  }
}

function observer1(state) {
  console.log(`state发生变化了,state is:${state}`);
}
function observer2(state) {
  if (state % 10 === 0)
    console.log(`state值是${state},是10的${state / 10}倍数`);
}

let s = new Subject(0);
let observer = new ObserverManager(s, [observer1, observer2]);
s.run();

发布/订阅模式

将观察者模式上面上面讲到的老师的监控—改成—>学生自己行为/状态变化后主动通知老师,这样子就是发布/订阅模式,显然上面的场景是不现实的。 换一个例子,你去书店买一本书,老板说这本书卖光了,还没来得及进货,过几天进货了再通知你。这就是发布/订阅模式。例子中你是订阅者(subscriber/observer),书店是发布者(subject);

  • subject:管理自身状态,并且在状态变更时,主动通知订阅者
  • subscriber/observer:在被通知后,执行相应动作

2.1 简单的发布订阅功能

继续沿用观察者模式中要实现的功能。subject的从1-100计时,在计数变更时,通知observer执行;其中subject还包括注册和移除观察者的功能。

/**
 * 发布订阅模式
 * 发布者 publish 和订阅者 subscriber
 *  */

class Subject {
  #state = 0;
  #observers = {};
  #timer = null;

  constructor(initState) {
    this.#state = initState;
  }
  getState() {
    return this.#state;
  }
  //状态变更启动
  run() {
    this.#count();
  }
  //模拟一个状态变化的函数,计数到100截止
  #count = () => {
    this.#timer = setTimeout(() => {
      this.#state++;
      this.#notify(this.#state);
      clearTimeout(this.#timer);
      this.#timer = null;
      if (this.#state < 100) {
        this.#count();
      }
    }, 100);
  };

  #notify = (state) => {
    Object.values(this.#observers).forEach((observer) => {
      observer(state);
    });
  };
  //如果eventName存在,则方法覆盖
  addObserver(eventName, eventHandler) {
    if (eventHandler instanceof Function) {
      if (this.#observers[eventName] === undefined) {
        this.#observers[eventName] = eventHandler;
      }
    } else {
      console.error(`非法的eventHandler`);
    }
  }
  
  removeObserver(eventName) {
    if (this.#observers[eventName]) {
      delete this.#observers[eventName];
    }
  }
}

function observer1(state) {
  console.log(`state发生变化了,state is:${state}`);
}
function observer2(state) {
  if (state % 10 === 0)
    console.log(`state值是${state},是10的${state / 10}倍数`);
}

const subject = new Subject(0);
subject.addObserver("监听整十数", observer2);
subject.run();

2.2 事件代理版本-增加事件管理器

上面班主任自己监控显得优点疲惫了,他想到了一个好办法,就是找一个学生作为纪律代表,当班里学生状态不好的时候,及时告知老师;这样的场景就是发布/订阅模式的事件代理版本。

实例代码,新增EventManager,将原本subject中处理observer的部分都交由EventManager处理,代码的功能模块拆分的更细。

/**
 * 发布订阅模式
 * 发布者 publish 和订阅者 subscriber
 * 新增事件管理器,作为publiser和subscriber的通信管理工具
 *  */

class Subject {
  #state = 0;
  #timer = null;
  #eventManager = null;

  constructor(initState, eventManager) {
    this.#state = initState;
    this.#eventManager = eventManager;
  }
  getState() {
    return this.#state;
  }
  //状态变更启动
  run() {
    this.#count();
  }
  //模拟一个状态变化的函数,计数到100截止
  #count = () => {
    this.#timer = setTimeout(() => {
      this.#state++;
      this.#eventManager.notify(this.#state);
      clearTimeout(this.#timer);
      this.#timer = null;
      if (this.#state < 100) {
        this.#count();
      }
    }, 100);
  };
}

class EventManager {
  #observers = {};
  constructor() {
    this.#observers = {};
  }

  notify = (state) => {
    console.log("state", state);
    Object.values(this.#observers).forEach((observer) => {
      observer(state);
    });
  };

  //如果eventName存在,则方法覆盖
  addObserver(eventName, eventHandler) {
    if (eventHandler instanceof Function) {
      if (this.#observers[eventName] === undefined) {
        this.#observers[eventName] = eventHandler;
      }
    } else {
      console.error(`非法的eventHandler`);
    }
  }

  removeObserver(eventName) {
    if (this.#observers[eventName]) {
      delete this.#observers[eventName];
    }
  }
}

//定义观察者

function observer1(state) {
  console.log(`state发生变化了,state is:${state}`);
}
function observer2(state) {
  if (state % 10 === 0)
    console.log(`state值是${state},是10的${state / 10}倍数`);
}
const eventManager = new EventManager();
const subject = new Subject(0, eventManager);
eventManager.addObserver("监听整十数", observer2);
subject.run();


3.总结

总之观察者模式,是主动观察的过程,离不开轮询; 而发布/订阅模式,是注册和通知共同实现的。