欢迎来到 TypeScript 中的设计模式系列,它介绍了使用TypeScript进行Web开发的一些有用的设计模式。
系列文章如下:
- 设计模式:(Strategy Pattern)策略模式
- 设计模式:(Builder Pattern)生成器模式
- 设计模式:(Chain of Responsibility Pattern)责任链模式
- 适配器模式: (Adapter Pattern) 适配器模式
- 设计模式:(Template Method Pattern)模板方法模式
设计模式对 Web 开发人员来说非常重要,我们可以通过掌握它们来编写更好的代码。在本文中,我将使用TypeScript来介绍责任链模式。
观察者模式使用非常广泛,MutationObserver
,IntersectionObserver
, PerformanceObserver
,这些API中都可以看到观察者模式。此外,此模式还用于事件监控和数据响应。
观察者模式定义了一对多的关系,允许多个观察者对象同时监控一个主体-对象。当subject-object的状态发生变化时,会通知到所有的观察者对象,以便他们可以自动更新。
观察者模式中有两个主要角色:Subject 和 Observer 为了更好的理解他们,我们先看下UML类图:
在上图中,我们分别定义了两个用于描述Observer
和 Subject
对象的两个接口。
interface Observer {
notify(article: Article): void;
}
interface Subject {
observers: Observer[];
addObserver(observer: Observer): void;
deleteObserver(observer: Observer): void;
notifyObservers(article: Article): void;
}
然后我们定义两个类分别实现以上两个接口。
ConcreteObserver 类
class ConcreteObserver implements Observer {
constructor(private name: string) {}
notify(article: Article) {
console.log(`"Article: ${article.title}" has been sent to ${this.name}.`);
}
}
ConcreteSubject 类
class ConcreteSubject implements Subject {
public observers: Observer[] = [];
public addObserver(observer: Observer): void {
this.observers.push(observer);
}
public deleteObserver(observer: Observer): void {
const n: number = this.observers.indexOf(observer);
n != -1 && this.observers.splice(n, 1);
}
public notifyObservers(article: Article): void {
this.observers.forEach((observer) => observer.notify(article));
}
}
接下来验证对应函数
const subject: Subject = new ConcreteSubject();
const chris1993 = new ConcreteObserver('Chris1993');
const bytefish = new ConcreteObserver('Bytefish');
subject.addObserver(chris1993);
subject.addObserver(bytefish);
subject.notifyObservers({
author: 'Bytefer',
title: 'Observer Pattern in TypeScript',
url: 'https://juejin.cn/***',
});
subject.deleteObserver(bytefish);
subject.notifyObservers({
author: 'Bytefer',
title: 'Adapter Pattern in TypeScript',
url: 'https://juejin.cn/***',
});
打印结果为:
"Article: Observer Pattern in TypeScript" has been sent to Chris1993.
"Article: Observer Pattern in TypeScript" has been sent to Bytefish.
"Article: Adapter Pattern in TypeScript" has been sent to Chris1993.
(Publish–subscribe Pattern)发布订阅模式
在软件架构中,发布-订阅是一种消息传递范式,其中消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。相反,发布的消息被分成不同的类别并发送给不同的订阅者。同样,订阅者可以表达对一个或多个类别的兴趣,并且只接收感兴趣的消息而不知道存在哪些发布者。
发布-订阅模型中有三个主要角色:(Publishers)发布者、(Channels)频道和(Subscribers)订阅者。
让我们基于发布-订阅模式实现一个 EventEmitter:
type EventHandler = (...args: any[]) => any;
class EventEmitter {
private c = new Map<string, EventHandler[]>();
subscribe(topic: string, ...handlers: EventHandler[]) {
let topics = this.c.get(topic);
if (!topics) {
this.c.set(topic, (topics = []));
}
topics.push(...handlers);
}
unsubscribe(topic: string, handler?: EventHandler): boolean {
if (!handler) {
return this.c.delete(topic);
}
const topics = this.c.get(topic);
if (!topics) {
return false;
}
const index = topics.indexOf(handler);
if (index < 0) {
return false;
}
topics.splice(index, 1);
if (topics.length === 0) {
this.c.delete(topic);
}
return true;
}
publish(topic: string, ...args: any[]): any[] | null {
const topics = this.c.get(topic);
if (!topics) {
return null;
}
return topics.map((handler) => {
try {
return handler(...args);
} catch (e) {
console.error(e);
return null;
}
});
}
}
对于 EventEmitter
的使用
const eventEmitter = new EventEmitter();
eventEmitter.subscribe('ts', (msg) => console.log(`Received:${msg}`));
eventEmitter.publish('ts', 'Observer pattern');
eventEmitter.unsubscribe('ts');
eventEmitter.publish('ts', 'Pub-Sub pattern');
运行会在控制台打印:Received:Observer pattern
在事件驱动的架构中,发布-订阅模式起着重要的作用。该模式的具体实现可以作为事件的总线,实现同一系统中不同组件或模块之间的消息通信。对于广泛使用的插件架构,可以用来实现在不同插件之间进行消息通信。