关于 observer-pattern
这是一份 JavaScript 设计模式指南,介绍可观察对象通知订阅者接收事件数据。适合构建松耦合、事件驱动组件的开发者,并说明订阅管理与性能考量。
主要功能
- 01订阅和取消订阅观察者
- 02向订阅者发送事件数据
- 03保持组件松耦合
- 04处理异步事件驱动数据
适用场景
- 01用户交互时通知日志记录器和提示框
- 02向组件广播下载完成事件
- 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 内置了大量与可观察模式配合使用的功能和示例。
优点
使用观察者模式是实现关注点分离和单一职责原则的好方法。观察者对象与可观察对象之间没有紧密耦合,并且可以随时解除耦合或重新耦合。可观察对象负责监控事件,而观察者只需处理接收到的数据。
缺点
如果观察者变得过于复杂,在通知所有订阅者时可能会导致性能问题。