Jansiel Notes

JavaScript设计模式:观察者模式

4.gif

模式概念

在软件系统中,一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,他们之间将产生联动。

观察者模式:

  • 定义了对象之间一种 一对多 的依赖关系,使得 一个对象的状态改变时,其相关依赖对象皆得到通知并被自动更新
  • 发生改变的对象称为 观察目标,被通知的对象称为 观察者
  • 一个观察目标可以对应一个或者多个观察者
  • 观察者模式是一种 对象行为型模式

模式结构

Snipaste_2024-07-21_20-43-15.png

  • 观察者模式包含两个继承结构:观察目标和具体观察目标的继承结构,观察者和具体观察者的继承结构。
  • 观察目标里面有一个用于存放所有观察者的集合 observers,以及添加、删除观察者的方法,还有一个 notify 方法,当观察目标状态发生更改时,通知所有的观察者,让其做出响应。
  • 观察者里面有一个 update 方法,就是作出响应的方法,而具体观察者里面的 update 方法即是不同的观察者做出响应的具体实现。
  • 观察者响应的同时,可能会反向访问到观察目标里面的属性,进行相应的数据设置和获取。

在实际的软件开发中,大部分我们采用的是简化版的观察者模式的结构,也就意味着我们只定义一个观察目标类, 观察目标没有继承结构,通过观察目标里面的添加、删除、通知观察者集合等方法,而在不同的观察者类里面实现了不同的响应。

观察者模式包含角色如下:

  • Subject 目标
  • ConcreteSubject 具体目标
  • Observer 观察者
  • ConcreteObserver 具体观察者

模式分析

  • 有时候在 具体观察者类ConcreteObserver 中需要使用到 具体目标类ConcreteSubject 中的状态(属性),会存在关联或依赖关系。
  • 如果在具体层之间具有关联关系,系统的 扩展性 将受到一定的影响:增加新的具体目标类有时候需要修改原有观察者的代码,在一定程度上违背了开闭原则,但是如果原有观察者类无须关联新增的具体目标,则系统扩展性不受影响。
  • 也就是当我们可能需要反方访问的时候,会出现以上这种影响扩展性的行为。

代码实现

这里结合一个例子实现该模式:

猫、老鼠、狗的故事:假设猫是老鼠和狗的观察目标,老鼠和狗是观察者,猫叫老鼠跑,狗跟着叫。

 1//观察目标类:猫
 2class Subject {
 3  constructor() {
 4    this.observers = []; //观察者集合
 5  }
 6  //添加观察者
 7  add(observer) {
 8    this.observers.push(observer);
 9  }
10  //移除观察者
11  delete(name) {
12    this.observers = this.observers.filter(e => e.name !== name);
13  }
14  //简化版的观察者模式,直接在抽象观察目标中实现notify方法,通知观察者
15  notify() {
16    this.observers.forEach(item => {
17      item.update();
18    });
19  }
20}
21
22//抽象观察者
23class Observer {
24  constructor(name) {
25    this.name = name;
26  }
27  //观察者更新业务
28  update() {
29    console.log(`观察者${this.name}做出响应`);
30  }
31}
32//具体观察者老鼠
33class MouseObserver extends Observer {
34  constructor(name) {
35    super(name);
36  }
37  run() {
38    console.log(`${this.name}跑起来`);
39  }
40  update() {
41    console.log(`观察者${this.name}做出响应`);
42    this.run();
43  }
44}
45//具体观察者狗
46class DogObserver extends Observer {
47  constructor(name) {
48    super(name);
49  }
50  call() {
51    console.log(`${this.name}汪汪叫`);
52  }
53  update() {
54    console.log(`观察者${this.name}做出响应`);
55    this.call();
56  }
57}
58
59const cat = new Subject();
60
61const mouse = new MouseObserver("杰瑞");
62const dog = new DogObserver("旺财");
63cat.add(mouse);
64cat.add(dog);
65
66//猫叫
67cat.notify();
68// 观察者杰瑞做出响应
69// 杰瑞跑起来
70// 观察者旺财做出响应
71// 旺财汪汪叫
72

模式的效果和应用

观察者模式优点:

  • 可以实现 表示层和数据逻辑层 的分离
  • 在观察目标和观察者之间建立一个 抽象的耦合
  • 支持 广播通信,简化了一对多系统设计的难度
  • 符合开闭原则,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便

观察者模式缺点:

  • 将所有的观察者都通知到会 花费很多时间
  • 如果 存在循环依赖 时可能导致系统崩溃
  • 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而只是知道观察目标发生了变化

在以下情况可以使用观察者模式:

  • 一个抽象模型有两个方面,其中 一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用
  • 一个对象的改变将导致一个或多个其他对象发生改变,且并 不知道具体有多少对象将发生改变,也不知道这些对象是谁
  • 需要在系统中 创建一个触发链

观察者模式实际应用:

mvc架构:将模型层看作是观察目标,视图层看作是观察者。

Snipaste_2024-07-21_22-00-29.png

6.gif