是时候拿出高级的技术了——观察者模式
背景介绍
这是设计模式系列的第四节,学习的是patterns.dev里设计模式中观察者模式内容,由于是资料是英文版,所以我的学习笔记就带有翻译的性质,但并不是翻译,记录的是自己的学习过程和理解。
第二节:JS和迪丽热巴一样有专业替身?没听过的快来补补课...
第三节:还在层层传递props?来学学非常实用的供应商模式吧
第四节:都知道JavaScript原型,但设计模式里的原型模式你会用吗?
第五节:React Hooks时代,怎么实现视图与逻辑分离呢?
写在前面
观察者模式,顾名思义就像监视者一直在观察某个人的一举一动,在编程里就是观察者通过订阅获得事件通知权限,事件发生时发消息通知所有已注册的观察者。观察者模式是一个很经典,应用很广泛的模式,常见的有新手消息、邮件订阅、RSS Feeds等都是经典实用场景。
人通过观察可以主动发现目标对象的变化,但是观察者模式则是需要被动接受通知!
极简释义
观察者通过订阅获得事件通知权限,事件发生时发消息通知所有已注册的观察者
正文开始
在程序中观察者模式具体由哪几部分构成呢?
- 观察者:
- 订阅事件:
- 取消订阅事件:
- 消息通知:
通过es6的class关键字可以很轻松的创建被观察者模式:
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方法通知全部的观察者。
然后我们可以在需要的地方使用观察者模式:
import React from "react";
import { Button, Switch, FormControlLabel } from "@material-ui/core";
import { ToastContainer, toast } from "react-toastify";
import observable from "./Observable";
function handleClick() {
observable.notify("User clicked button!");
}
function handleToggle() {
observable.notify("User toggled switch!");
}
function logger(data) {
console.log(`${Date.now()} ${data}`);
}
function toastify(data) {
toast(data, {
position: toast.POSITION.BOTTOM_RIGHT,
closeButton: false,
autoClose: 2000
});
}
observable.subscribe(logger);
observable.subscribe(toastify);
export default function App() {
return (
<div className="App">
<Button variant="contained" onClick={handleClick}>
Click me!
</Button>
<FormControlLabel
control={<Switch name="" onChange={handleToggle} />}
label="Toggle me!"
/>
<ToastContainer />
</div>
);
}
通过点击Button和Switch分别发布不同的消息,通过subscribe方法订阅所有事件对象会被执行;

观察者模式十分有用,特别是异步的,基于事件的数据处理:
- 下载完成时通知某些组件;
- 留言通知所有成员。
经典案例学习 ——— RxJs
RxJs是一个非常流行的js库:
ReactiveX将Observer模式与Iterator模式以及函数式编程结合起来,从而高效地管理事件序列。
在RxJs中,我们创建观察者模式,并订阅某个事件;下面我们来看一个检测用户是否拖拽Dom的小例子:
import React from "react";
import ReactDOM from "react-dom";
import { fromEvent, merge } from "rxjs";
import { sample, mapTo } from "rxjs/operators";
import "./styles.css";
merge(
fromEvent(document, "mousedown").pipe(mapTo(false)),
fromEvent(document, "mousemove").pipe(mapTo(true))
)
.pipe(sample(fromEvent(document, "mouseup")))
.subscribe(isDragging => {
console.log("Were you dragging?", isDragging);
});
ReactDOM.render(
<div className="App">Click or drag anywhere and check the console!</div>,
document.getElementById("root")
);
在线示例 RxJs里有很多内置的特性和案例使用到了观察者模式,有兴趣的可以看看源码!
优缺点分析
优点:
观察者模式有很多优点,大概总结下:
- 分离关注点SOC (separation of concerns);
- 满足单一指责原则;
- 解耦合,观察者和事件对象解耦合;
被观察的对象通常是一些交互事件或异步事件,观察者通常处理接收到的数据。
缺点:
观察者模式如果变得太复杂臃肿,在通知所有订阅者时可能会导致性能问题。
同义辨析————观察者模式和订阅发布模式
观察者模式应用于Observable对象,观察者和事件消息直接交互;
观察者模式就像你要跳槽,心中有了目标公司,你就会关注目标公司的招聘信息,当发现有新招聘时就会行动;
订阅发布模式应用于事件消息,发布者和订阅者完全解耦,通过调度中心交互;
订阅发布模式则是你要跳槽,你在招聘网站修改在职状态,然后很多公司在招聘网站上发布招聘信息,然后招聘网站把这些招聘信息推送到你面前。
转载自:https://juejin.cn/post/7187206768454271032