mcpField
O
observer-pattern

observer-pattern

github.com/patternsdev开发257639 次安装28 周前更新
代码仓库

关于 observer-pattern

这是一份 JavaScript 设计模式指南,介绍可观察对象通知订阅者接收事件数据。适合构建松耦合、事件驱动组件的开发者,并说明订阅管理与性能考量。

主要功能

  1. 01订阅和取消订阅观察者
  2. 02向订阅者发送事件数据
  3. 03保持组件松耦合
  4. 04处理异步事件驱动数据

适用场景

  1. 01用户交互时通知日志记录器和提示框
  2. 02向组件广播下载完成事件
  3. 03向订阅者共享新消息事件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/patternsdev/skills --skill observer-pattern

安装后在 Claude Code 中输入/observer-pattern确认已加载。

详细介绍

观察者模式

目录

使用观察者模式,我们可以让某些对象(即观察者)_订阅_另一个称为可观察对象的对象。每当发生事件时,可观察对象都会通知所有观察者!

适用场景

  • 当你需要将状态变化或事件通知应用程序的多个部分时,使用此模式
  • 这有助于实现组件之间由事件驱动的异步通信

不适用场景

  • 订阅者数量极多,且通知性能至关重要时
  • 对于一对一通信,简单的回调或直接函数调用就足够时
  • 隐式事件链带来的调试困难超过了松耦合的益处时

说明

  • 创建一个 Observable 类,其中包含 subscribe、unsubscribe 和 notify 方法
  • 让观察者与可观察对象保持松耦合,以更好地划分职责
  • 向大量订阅者发送通知且涉及复杂逻辑时,请注意性能
  • 对于更高级的响应式编程需求,可以考虑使用 RxJS 等库

详情

可观察对象通常包含 3 个重要部分:

  • observers:一个观察者数组;每当发生特定事件时,这些观察者都会收到通知
  • subscribe():用于将观察者添加到观察者列表的方法
  • unsubscribe():用于从观察者列表中移除观察者的方法
  • notify():每当发生特定事件时,用于通知所有观察者的方法

让我们使用 ES6 类创建一个可观察对象:

class Observable {
  constructor() {
    this.observers = [];
  }

  subscribe(func) {
    this.observers.push(func);
  }

  unsubscribe(func) {
    this.observers = this.observers.filter((observer) => observer !== func);
  }

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

现在,我们可以使用 subscribe 方法将观察者添加到观察者列表中,使用 unsubscribe 方法移除观察者,并使用 notify 方法通知所有订阅者。

让我们用这个可观察对象构建一些内容。我们有一个非常基础的应用,只包含两个组件:Button 和 Switch。

export default function App() {
  return (
    <div className="App">
      <Button>Click me!</Button>
      <FormControlLabel control={<Switch />} />
    </div>
  );
}

我们想要跟踪用户与应用的交互。每当用户点击按钮或切换开关时,我们都想记录该事件及其时间戳。除了记录事件,我们还想创建一条事件发生时显示的 Toast 通知!

每当用户调用 handleClick 或 handleToggle 函数时,这些函数都会调用观察者上的 notify 方法。notify 方法会将 handleClick 或 handleToggle 函数传入的数据通知给所有订阅者!

首先,让我们创建 logger 和 toastify 函数。这些函数最终会接收来自 notify 方法的 data。

import { ToastContainer, toast } from "react-toastify";

function logger(data) {
  console.log(`${Date.now()} ${data}`);
}

function toastify(data) {
  toast(data);
}

export default function App() {
  return (
    <div className="App">
      <Button>Click me!</Button>
      <FormControlLabel control={<Switch />} />
      <ToastContainer />
    </div>
  );
}

目前,logger 和 toastify 函数还不知道可观察对象的存在:可观察对象还无法通知它们!要让它们成为观察者,我们需要使用可观察对象上的 subscribe 方法将它们_订阅_起来!

import { ToastContainer, toast } from "react-toastify";

function logger(data) {
  console.log(`${Date.now()} ${data}`);
}

function toastify(data) {
  toast(data);
}

observable.subscribe(logger);
observable.subscribe(toastify);

export default function App() {
  return (
    <div className="App">
      <Button>Click me!</Button>
      <FormControlLabel control={<Switch />} />
      <ToastContainer />
    </div>
  );
}

每当发生事件时,logger 和 toastify 函数都会收到通知。现在,我们只需要实现实际通知可观察对象的函数:handleClick 和 handleToggle 函数!这些函数应调用可观察对象上的 notify 方法,并传入观察者应接收的数据。

import { ToastContainer, toast } from "react-toastify";

function logger(data) {
  console.log(`${Date.now()} ${data}`);
}

function toastify(data) {
  toast(data);
}

observable.subscribe(logger);
observable.subscribe(toastify);

export default function App() {
  function handleClick() {
    observable.notify("User clicked button!");
  }

  function handleToggle() {
    observable.notify("User toggled switch!");
  }

  return (
    <div className="App">
      <Button>Click me!</Button>
      <FormControlLabel control={<Switch />} />
      <ToastContainer />
    </div>
  );
}

我们刚刚完成了整个流程:handleClick 和 handleToggle 使用数据调用观察者上的 notify 方法,随后观察者会通知订阅者;在这个例子中,订阅者是 logger 和 toastify 函数。

每当用户与任一组件交互时,logger 和 toastify 函数都会收到通知,并获得我们传给 notify 方法的数据!

虽然我们可以通过多种方式使用观察者模式,但在处理异步、基于事件的数据时,它会非常实用。也许你希望某些数据下载完成时通知特定组件,或者用户向留言板发送新消息时通知所有其他成员。

案例研究

RxJS 是一个广受欢迎的使用可观察模式的库。RxJS 内置了大量与可观察模式配合使用的功能和示例。

优点

使用观察者模式是实现关注点分离和单一职责原则的好方法。观察者对象与可观察对象之间没有紧密耦合,并且可以随时解除耦合或重新耦合。可观察对象负责监控事件,而观察者只需处理接收到的数据。

缺点

如果观察者变得过于复杂,在通知所有订阅者时可能会导致性能问题。

来源

参考资料

评论

支持 Markdown