前端框架中的设计模式

74 阅读6分钟

本文将简述常见前端设计模式思想,对课程内知识点进行总结的同时,对一段实例代码详细分析,希望本文在后来者学习设计模式尝试分析时能够为思考和理解提供方向和思考方向上的引导

1. 常见设计模式思想的快速浏览

面向对象的设计模式

  • 创建型 – 如何创建一个对象
  • 结构型 – 如何灵活的将对象组装成较大的结构
  • 行为型 – 负责对象间的高效通信和职责划分

浏览器中的设计模式

  • 单例模式
    • 定义:全局唯一访问对象
    • 应用场景:缓存,全局状态管理等
  • 发布订阅模式
    • 定义:一种订阅机制,可在被订阅对象发生变化时通知订阅者。
    • 应用场景:从系统架构之间的解耦,到业务中一些实现模式,像邮件订阅、上线订阅等等,应用广泛。

JavaScript中的设计模式

  • 原型模式

    • 定义:复制已有对象来创建新的对象
    • 应用场景:JS中对象创建的基本模式
    • 思想:创建一个原型,用于变化,类似于class
  • 代理模式

    • 可以自定义来对原对象的访问方式,并且允许更新前后的一些额外处理
    • 代理工具,前端框架实现,监控等等
  • 迭代器模式

    • 不暴露数据类型的情况下访问集合的数据
    • 为数据结构提供操作接口
    • 类似Java中定义一个iterator,以使用for each(JS中为for of)

前端框架中的设计模式

  • 代理模式

代理模式的核心思想是通过一个代理对象控制对目标对象的访问。代理对象作为中间层,可以对目标对象的行为进行扩展、控制或修改,而不需要改变目标对象的代码。同上述的代理模式

  • 组合模式

组合模式的核心思想是对象间可以互相组合为对象,使得客户端可以一致地处理单个对象和对象的组合。它是一种结构型设计模式,适用于需要以统一方式处理 部分-整体关系 的场景。

2. 上述部分设计模式的实例分析

和课程中所述的一致,设计模式重在思想的理解,而非具体的场景,并且文章长度不宜太长,我就挑选发布订阅模式为进行实例分析,事实上其他模式对我来说也都比较好理解

发布订阅模式实现用户订阅事件的代码分析

User类实现

// 定义一个类型 Notify,它是一个接受 User 对象的函数,返回值为 void
type Notify = (user: User) => void;

// 导出一个 User 类
export class User {
  name: string; // 用户的名字
  status: "offline" | "online"; // 用户的状态,可以是 "offline" 或 "online"
  followers: { user: User; notify: Notify }[]; // 存储订阅该用户的关注者信息,包括用户和回调函数

  // 构造函数,初始化用户的名字、状态和关注者列表
  constructor(name: string) {
    this.name = name;
    this.status = "offline"; // 默认状态为 "offline"
    this.followers = []; // 初始化关注者列表为空
  }

  // 用户订阅另一个用户
  subscribe(user: User, notify: Notify) {
    // 将订阅者和回调函数添加到目标用户的关注者列表
    user.followers.push({ user: this, notify });
  }

  // 将用户状态设置为在线,并通知所有关注者
  online() {
    this.status = "online"; // 更新状态为 "online"

    // 遍历所有关注者,调用每个关注者的回调函数并传递当前用户对象
    this.followers.forEach(({ notify }) => {
      notify(this);
    });
  }
}

测试用例

// 测试:当用户上线时,应该通知订阅的关注者
test("should notify followers when user is online for multiple users", () => {
  // 创建三个用户实例
  const user1 = new User("user1");
  const user2 = new User("user2");
  const user3 = new User("user3");

  // 创建两个模拟的通知函数(用于检查是否被调用)
  const mockNotifyUser1 = jest.fn(); // user1 的通知函数
  const mockNotifyUser2 = jest.fn(); // user2 的通知函数

  // user1 和 user2 订阅 user3,分别使用各自的回调函数
  user1.subscribe(user3, mockNotifyUser1);
  user2.subscribe(user3, mockNotifyUser2);

  // 让 user3 上线
  user3.online();

  // 验证 mockNotifyUser1 是否被调用,并且传递了 user3
  expect(mockNotifyUser1).toBeCalledWith(user3);

  // 验证 mockNotifyUser2 是否被调用,并且传递了 user3
  expect(mockNotifyUser2).toBeCalledWith(user3);
});
### 主要逻辑:

1. 第一段代码定义了一个用户的订阅机制:`subscribe` 方法用于订阅其他用户,`online` 方法用于将自身状态设为在线并通知所有关注者。
2. 第二段代码是测试用例,验证了当用户上线时,所有订阅者都会收到通知(通过模拟函数验证调用情况)。

### 发布订阅模式在代码中的体现

#### 1. **订阅(Subscribe)**

```typescript
subscribe(user: User, notify: Notify) {
    user.followers.push({ user: this, notify });
}
  • 这里的subscribe()方法允许某个User(订阅者)订阅另一个User(发布者)。
  • 每次订阅时,发布者将订阅者和其对应的回调函数notify存储在followers数组中。
  • followers数组相当于事件管理器,保存了所有订阅者的信息。

2. 发布(Online)
online() {
    this.status = "online"; // 将用户状态更新为在线
    this.followers.forEach(({ notify }) => {
        notify(this); // 通知每个订阅者
    });
}
  • 当用户调用online()方法时,发布者变为"online"状态,并通过遍历followers数组通知所有订阅者。
  • notify(this)调用的本质是执行订阅者的回调函数,并将当前用户(即发布者)作为参数传递。

3. 通知订阅者

在测试代码中:

const mockNotifyUser1 = jest.fn();
const mockNotifyUser2 = jest.fn();

user1.subscribe(user3, mockNotifyUser1); // user1 订阅 user3
user2.subscribe(user3, mockNotifyUser2); // user2 订阅 user3

user3.online(); // user3 上线时发布消息
  • user3调用online()时:
    • mockNotifyUser1mockNotifyUser2 这两个回调函数被执行,分别通知user1user2,完成消息发布。

发布订阅模式的三个关键步骤在代码中的实现是:

  1. 订阅(Subscribe):通过subscribe()方法将订阅者及其回调函数注册到发布者的followers列表。
  2. 发布(Publish):通过online()方法通知所有订阅者。
  3. 事件通知:通过执行订阅者的回调函数notify实现事件的实际响应。

3. 总结

不同设计模式之间采用了截然不同的代码思路,与其讨论其优缺点的对比,理解不同设计模式的适用场景(前文有所体积)才是最重要的,设计模式的思想对于代码的维护可以减少大量的负担,也提供了一些清晰代码思路,但将其运用熟练的话,恐怕需要大量的项目经验,任重道远!