定义
观察者模式,是一种行为设计模式,它允许将被观察者管理其观察者列表,并在状态变化时通知它们。
这种模式在实现事件监听和发布/订阅系统中非常有用。
UML 类图
typescript 实现
1. 定义观察者/订阅者接口
interface Subscriber {
update(publisher: Publisher): void;
}
2. 创建被观察者/发布者类
interface Publisher {
subscribe(subscriber: Subscriber): void;
unsubscribe(subscriber: Subscriber): void;
notify(): void;
}
class ConcretePublisher implements Publisher {
private subscribers: Subscriber[] = [];
private state: number;
public getState(): number {
return this.state;
}
public setState(state: number): void {
this.state = state;
this.notify();
}
public subscribe(subscriber: Subscriber): void {
const isExist = this.subscribers.includes(subscriber);
if(isExist) {
console.log("Publisher: Subscriber has been subscribed already.");
}
console.log("Publisher: subscribe an subscriber.");
this.subscribers.push(subscriber);
}
public unsubscribe(subscriber: Subscriber): void {
const subscriberIndex = this.subscribers.indexOf(subscriber);
if(subscriberIndex === -1) {
return console.log("Publisher: Noexistent subscriber.");
}
this.subscribers.splice(subscriberIndex, 1);
console.log("Publisher: unsubscribe an subscriber.");
}
public notify(): void {
console.log("Publisher: Notifying subscribers...");
for(const subscriber of this.subscribers) {
subscriber.update(this);
}
}
}
3. 创建具体观察者类
class ConcreteSubscriber implements Subscriber {
public update(publisher: Publisher): void {
if(publisher instanceof ConcretePublisher && publisher.getState() < 3) {
console.log("ConcreteSubscriber: Reached to the event.");
}
}
}
4. 使用示例
const publisher = new ConcretePublisher();
const subscriber = new ConcreteSubscriber();
publisher.subscribe(subscriber);
const subscriber2 = new ConcreteSubscriber();
publisher.subscribe(subscriber2);
publisher.setState(2);
publisher.setState(3);
publisher.unsubscribe(subscriber2);
publisher.setState(4);
通用实现
javascript 实现的事件模型,订阅者不必须是类(函数是第一等公民),下面基本是模仿 Event 系统
// 公共代码
export class EventTarget {
private listeners: Record<
string,
Map<EventListenerObject | EventListener, AddEventListenerOptions>
> = {};
addEventListener(
type: string,
callback: EventListenerOrEventListenerObject,
options?: AddEventListenerOptions | boolean
): void {
if(!this.listeners[type]) {
this.listeners[type] = new Map();
}
const eventListenerOptions: AddEventListenerOptions = typeof options === "object"
? options
: { capture: Boolean(options) }
this.listeners[type]!.set(callback, eventListenerOptions);
if(eventListenerOptions.signal) {
eventListenerOptions.signal.addEventListener("abort", () => {
this.removeEventListener(type, callback);
});
}
}
removeEventListener(
type: string,
callback: EventListenerOrEventListenerObject | null,
options?: EventListenerOptions | boolean
): void {
if(this.listeners[type]) {
this.listeners[type]!.delete(callback as EventListener);
}
}
dispatchEvent(event: Event): void {
if(event.type in this.listeners) {
const eventListeners = Array.from(this.listeners[event.type]);
for(const [listener, options] of eventListeners) {
if(options.capture && [0, 1, 2].includes(event.eventPhase)) {
if(typeof listener === "object" && listener.handleEvent) {
listener.handleEvent.call(listener, event);
} else if (typeof listener === "function") {
listener.call(this, event);
}
} else if(!options.capture && [0, 3, 2].includes(event.eventPhase)) {
if(typeof listener === "object" && listener.handleEvent) {
listener.handleEvent.call(listener, event);
} else if(typeof listener === "function") {
listener.call(this, event);
}
}
if(options.once) {
this.removeEventListener(event.type, listener);
}
}
}
}
}
// 私有代码,使用示例
class ButtonElement extends EventTarget {
}
const btn = new ButtonElement();
btn.addEventListener("click", () => {
console.log("click button");
});
function handleClick() {
btn.dispatchEvent(new Event("click"));
}
handleClick();