概念

The observer pattern defines a one-to-many relationship. When one object updates, it notifies many other objects that it has been updated.

  • subject – This is the object that will send out a notification to all of the ‘observers’ who want/need to know that the subject was updated.
  • observers – These are the objects that want to know when the subject has changed.

    设计模式要干的就是解耦。 创建型模式是将创建和使用代码解耦,结构型是将不同功能代码解耦,行为型模式是将不同代码解耦 具体到观察者就是将观察者和被观察者代码解耦

代码实现

  1. /**
  2. * The Subject interface declares a set of methods for managing subscribers.
  3. */
  4. interface Subject {
  5. // Attach an observer to the subject.
  6. attach(observer: Observer): void;
  7. // Detach an observer from the subject.
  8. detach(observer: Observer): void;
  9. // Notify all observers about an event.
  10. notify(): void;
  11. }
  12. /**
  13. * The Subject owns some important state and notifies observers when the state
  14. * changes.
  15. */
  16. class ConcreteSubject implements Subject {
  17. /**
  18. * @type {number} For the sake of simplicity, the Subject's state, essential
  19. * to all subscribers, is stored in this variable.
  20. */
  21. public state: number;
  22. /**
  23. * @type {Observer[]} List of subscribers. In real life, the list of
  24. * subscribers can be stored more comprehensively (categorized by event
  25. * type, etc.).
  26. */
  27. private observers: Observer[] = [];
  28. /**
  29. * The subscription management methods.
  30. */
  31. public attach(observer: Observer): void {
  32. const isExist = this.observers.includes(observer);
  33. if (isExist) {
  34. return console.log('Subject: Observer has been attached already.');
  35. }
  36. console.log('Subject: Attached an observer.');
  37. this.observers.push(observer);
  38. }
  39. public detach(observer: Observer): void {
  40. const observerIndex = this.observers.indexOf(observer);
  41. if (observerIndex === -1) {
  42. return console.log('Subject: Nonexistent observer.');
  43. }
  44. this.observers.splice(observerIndex, 1);
  45. console.log('Subject: Detached an observer.');
  46. }
  47. /**
  48. * Trigger an update in each subscriber.
  49. */
  50. public notify(): void {
  51. console.log('Subject: Notifying observers...');
  52. for (const observer of this.observers) {
  53. observer.update(this);
  54. }
  55. }
  56. /**
  57. * Usually, the subscription logic is only a fraction of what a Subject can
  58. * really do. Subjects commonly hold some important business logic, that
  59. * triggers a notification method whenever something important is about to
  60. * happen (or after it).
  61. */
  62. public someBusinessLogic(): void {
  63. console.log('\nSubject: I\'m doing something important.');
  64. this.state = Math.floor(Math.random() * (10 + 1));
  65. console.log(`Subject: My state has just changed to: ${this.state}`);
  66. this.notify();
  67. }
  68. }
  69. /**
  70. * The Observer interface declares the update method, used by subjects.
  71. */
  72. interface Observer {
  73. // Receive update from subject.
  74. update(subject: Subject): void;
  75. }
  76. /**
  77. * Concrete Observers react to the updates issued by the Subject they had been
  78. * attached to.
  79. */
  80. class ConcreteObserverA implements Observer {
  81. public update(subject: Subject): void {
  82. if (subject instanceof ConcreteSubject && subject.state < 3) {
  83. console.log('ConcreteObserverA: Reacted to the event.');
  84. }
  85. }
  86. }
  87. class ConcreteObserverB implements Observer {
  88. public update(subject: Subject): void {
  89. if (subject instanceof ConcreteSubject && (subject.state === 0 || subject.state >= 2)) {
  90. console.log('ConcreteObserverB: Reacted to the event.');
  91. }
  92. }
  93. }
  94. /**
  95. * The client code.
  96. */
  97. const subject = new ConcreteSubject();
  98. const observer1 = new ConcreteObserverA();
  99. subject.attach(observer1);
  100. const observer2 = new ConcreteObserverB();
  101. subject.attach(observer2);
  102. subject.someBusinessLogic();
  103. subject.someBusinessLogic();
  104. subject.detach(observer2);
  105. subject.someBusinessLogic();

Event Bus/ Event Emitter

  1. class EventEmitter {
  2. constructor() {
  3. // handlers是一个map,用于存储事件与回调之间的对应关系
  4. this.handlers = {}
  5. }
  6. // on方法用于安装事件监听器,它接受目标事件名和回调函数作为参数
  7. on(eventName, cb) {
  8. // 先检查一下目标事件名有没有对应的监听函数队列
  9. if (!this.handlers[eventName]) {
  10. // 如果没有,那么首先初始化一个监听函数队列
  11. this.handlers[eventName] = []
  12. }
  13. // 把回调函数推入目标事件的监听函数队列里去
  14. this.handlers[eventName].push(cb)
  15. }
  16. // emit方法用于触发目标事件,它接受事件名和监听函数入参作为参数
  17. emit(eventName, ...args) {
  18. // 检查目标事件是否有监听函数队列
  19. if (this.handlers[eventName]) {
  20. // 这里需要对 this.handlers[eventName] 做一次浅拷贝,主要目的是为了避免通过 once 安装的监听器在移除的过程中出现顺序问题
  21. const handlers = this.handlers[eventName].slice()
  22. // 如果有,则逐个调用队列里的回调函数
  23. handlers.forEach((callback) => {
  24. callback(...args)
  25. })
  26. }
  27. }
  28. // 移除某个事件回调队列里的指定回调函数
  29. off(eventName, cb) {
  30. const callbacks = this.handlers[eventName]
  31. const index = callbacks.indexOf(cb)
  32. if (index !== -1) {
  33. callbacks.splice(index, 1)
  34. }
  35. }
  36. // 为事件注册单次监听器
  37. once(eventName, cb) {
  38. // 对回调函数进行包装,使其执行完毕自动被移除
  39. const wrapper = (...args) => {
  40. cb(...args)
  41. this.off(eventName, wrapper)
  42. }
  43. this.on(eventName, wrapper)
  44. }
  45. }

参考资料

  1. 观察者模式
  2. How to Use the Observable Pattern in JavaScript
  3. 56 | 观察者模式(上):详解各种应用场景下观察者模式的不同实现方式
  4. React Architecture: The React Provider Pattern
  5. 观察者模式 —- 修言