跳到主要内容

行为型设计模式

职责链 (CHAIN OF RESPONSIBILITY)

使用场景
  • 多个对象可以处理同一请求,根据运行时动态确定对象;
结构
  • Handler:处理请求的接口;
  • ConcreteHandler;
    • 实现 Handler;
    • 若可以处理请求,处理该请求,反之转发给其后继者;
      • 定义 successor 属性存储后继者;

职责链

使用方法
  • 用户提交一个请求,沿职责链传递,直至一个 ConcreteHandler 处理该请求;
示例代码
// 创建 Handler 接口 Account
abstract class Account {
protected _successor: Account | null;
protected _name: string;
protected _balance: number;

constructor() {
this._balance = 0;
this._name = "account";
this._successor = null;
}

setNext(account: Account) {
this._successor = account;
}

// 若可以处理请求, 处理该请求, 反之转发给其后继者
pay(amountToPay: number) {
if (this.canPay(amountToPay)) {
console.log(`Paid ${amountToPay} using ${this._name}`);
} else if (this._successor) {
console.log(`Cannot pay using ${this._name}. Proceeding...`);
this._successor.pay(amountToPay);
} else {
console.log("None of the accounts have enough _balance");
}
}

canPay(amount: number) {
return this._balance >= amount;
}
}

// Account 的具体实现
class Bank extends Account {
constructor(balance: number) {
super();
this._name = "bank";
this._balance = balance;
}
}

class Paypal extends Account {
constructor(balance: number) {
super();
this._name = "Paypal";
this._balance = balance;
}
}

class Bitcoin extends Account {
constructor(balance: number) {
super();
this._name = "bitcoin";
this._balance = balance;
}
}

const bank = new Bank(100);
const paypal = new Paypal(200);
const bitcoin = new Bitcoin(300);

bank.setNext(paypal);
paypal.setNext(bitcoin);

// Cannot pay using bank. Proceeding...
// Cannot pay using Paypal. Proceeding...
// Paid 259 using bitcoin
bank.pay(259);

命令 (COMMAND)

使用场景
  • 用户使用不同的请求操作同一对象;
  • 支持取消,排队,日志的操作;
结构
  • Command:命令接口;
  • ConcreteCommand;
    • Command 接口的具体实现;
    • 使用组合将一个 Receiver 作为其属性;
    • 其不同方法中将 Receiver 与其一个动作绑定;
  • Invoker:接受不同 Command ,基于 Command 的不同接口触发对应动作;
  • Receiver;
    • 接受者;
    • 接受者具有自己的动作;

命令

使用方法
  • Receiver 作为 ConcreteCommand 的一个属性;
  • ConcreteCommand 实现具体命令;
  • Invoker 接受不同的 ConcreteCommand,通过 Command 的 Execute 动作提交请求;
  • ConcreteConcreteCommand 对 Receiver 执行具体动作;
示例代码
// 定义 Receiver
class Bulb {
turnOn() {
console.log("Bulb has been lit!");
}

turnOff() {
console.log("Darkness!");
}
}

// 定义 Command 接口
interface Command {
execute(): void;
undo(): void;
redo(): void;
}
// 实现 Command 接口
class TurnOn implements Command {
// 存储 Receiver
private _bulb: Bulb;

constructor(bulb: Bulb) {
this._bulb = bulb;
}

// 绑定 Receiver 与其动作
execute(): void {
this._bulb.turnOn();
}

undo(): void {
this._bulb.turnOff();
}

redo(): void {
this.execute();
}
}
class TurnOff implements Command {
private _bulb: Bulb;

constructor(bulb: Bulb) {
this._bulb = bulb;
}

execute(): void {
this._bulb.turnOff();
}

undo(): void {
this._bulb.turnOn();
}

redo(): void {
this.execute();
}
}

// 定义 Invoker
class RemoteController {
// 接受不同 Command , 基于命令触发对应动作
submit(command: Command) {
command.execute();
}
}

const bulb = new Bulb();
const turnOn = new TurnOn(bulb);
const turnOff = new TurnOff(bulb);
const remote = new RemoteController();

remote.submit(turnOn); // Bulb has been lit
remote.submit(turnOff); // Darkness!

解释器 (INTERPRETER)

使用场景
  • 解析自定义文法;
结构
  • AbstractExpression:定义 AST 中抽象的解释操作;
  • TerminalExpression:定义终结符的操作;
  • NonterminalExpression:定义非终结符的操作;
  • Context:上下文信息;
  • Client:定义一个 AST;

解释器

使用方法
  • 详见编译原理;
示例代码
  • 详见编译原理;

迭代器 (ITERATOR)

使用场景
  • 迭代对象内部元素,但又不暴露内部实现;
  • 为多种不用的迭代对象提供同一接口;
结构
  • Iterator:迭代器接口;
  • ConcreteIterator:Iterator 接口的具体实现;
  • Aggregate:创建 Iterator 接口;
    • 可不实现;
  • ConcreteAggregate:实现 Aggregate 接口;

迭代器

使用方法
  • 使用 ConcreteAggregate 创建 ConcreteIterator;
  • 使用 ConcreteIterator 迭代对象;
示例代码
// 迭代对象
class RadioStation {
protected _frequency: number;

constructor(frequency: number) {
this._frequency = frequency;
}

getFrequency(): number {
return this._frequency;
}
}

// 定义 Iterator 接口
interface Iterator<T> {
first(): T | null;
next(): T | null;
isDone(): boolean;
currentItem(): T | null;
}
// 实现 Iterator 接口
class RadioStationIterator implements Iterator<RadioStation> {
protected _stations: RadioStation[];
protected _counter: number;

constructor(stations: RadioStation[]) {
this._counter = 0;
this._stations = stations;
}

currentItem(): RadioStation | null {
if (this.isDone()) {
return null;
} else {
return this._stations[this._counter];
}
}

first(): RadioStation | null {
if (this.isDone()) {
return null;
} else {
return this._stations[0];
}
}

next(): RadioStation | null {
if (this.isDone()) {
return null;
} else {
this._counter++;
return this._stations[this._counter];
}
}

isDone(): boolean {
return (
this._stations.length === 0 || this._counter === this._stations.length - 1
);
}
}

const stations = [
new RadioStation(1),
new RadioStation(2),
new RadioStation(3),
new RadioStation(4),
];

let iterator = new RadioStationIterator([]);
console.log(iterator.currentItem()); // null
iterator = new RadioStationIterator(stations);
console.log(iterator.currentItem()); // RadioStation { _frequency: 1 }
console.log(iterator.next()); // RadioStation { _frequency: 2 }
console.log(iterator.next()); // RadioStation { _frequency: 3 }
console.log(iterator.next()); // RadioStation { _frequency: 4 }
console.log(iterator.next()); // null
console.log(iterator.isDone()); // true

中介者 (MEDIATOR)

使用场景
  • 使用一个中介者封装不同对象之间的交互;
  • 保证各对象之间的低耦合;
结构
  • Mediator:定义中介者接口;
  • ConcreteMediator:实现 Mediator 接口;
  • Colleague:定义同事类接口;
    • 使用组合的方式将 Mediator 作为其属性;
  • ConcreteColleague:实现 Colleague 接口;

中介者

使用方法
  • Colleague 通过 Mediator 转发请求实现不同 Colleague 的协作;
示例代码
// 定义中介者接口
interface ChatRoomMediator {
showMessage(user: User, message: string): void;
}
// 实现 Mediator 接口
class ChatRoom implements ChatRoomMediator {
showMessage(user: User, message: string): void {
console.log(`${user.getName()}: ${message}`);
}
}

// 定义同事类接口
interface IUser {
getName(): string;
send(message: string): void;
}
// 实现 Colleague 接口
// 使用组合的方式将 Mediator 作为其属性
class User implements IUser {
protected _name: string;
protected _chatMediator: ChatRoomMediator;

constructor(name: string, mediator: ChatRoomMediator) {
this._chatMediator = mediator;
this._name = name;
}

getName() {
return this._name;
}

send(message: string): void {
this._chatMediator.showMessage(this, message);
}
}

const mediator = new ChatRoom();

const john = new User("john", mediator);
const Jake = new User("Jake", mediator);

john.send("Hello"); // john: Hello
Jake.send("Hey"); // Jake: Hey

备忘录 (MEMENTO)

使用场景
  • 不破坏封装性的前提下,保存对象内部状态,从而实现恢复机制;
结构
  • Memento:存储对象内部状态;
  • Originator:原发器;
    • 创建一个备忘录;
    • 使用备忘录恢复内部状态;
  • Caretaker:备忘录管理者;
    • 只读备忘录;

备忘录

使用方法
  • 向 Originator 发送请求,创建 Memento,并将 Memento 以组合形式传递给 Caretaker;
  • 一定时间后,Originator 通过 Caretaker 读取的 Memento 恢复状态;
示例代码
// 备忘录
class Memento {
public state: any;

constructor(state: any) {
this.state = state;
}

public getState() {
return this.state;
}
}

// 备忘录管理者
class Caretaker {
private stack: Memento[] = [];

public getMemento() {
return this.stack.pop();
}

public addMemento(memoto: Memento) {
this.stack.push(memoto);
}
}

// 发起者
class Originator {
private state: any;

public getState() {
return this.state;
}

public setState(state: any) {
this.state = state;
}

public createMemoto() {
return new Memoto(this.state);
}

public setMemoto(memoto: Memoto) {
this.state = memoto.getState();
}
}

const originator = new Originator();
const caretaker = new Caretaker();
originator.setState("hello world");
caretaker.setMemento(originator.createMento());
originator.setMemento(caretaker.getMemento());

观察者 (OBSERVER)

使用场景
  • 一个对象的状态发生改变,依赖于他的对象得到通知并更新;
结构
  • Subject:目标;
    • 提供注册和删除 Observer 的接口;
  • ConcreteSubject:Subject 接口的具体实现;
  • Observer:观察者接口;
  • ConcreteObserver:Observer 的具体实现;
    • 维护一个执行 ConcreteSubject 的引用;
    • 存储状态;
    • 通过 update 接口更新状态;

观察者

使用方法
  • Subject 注册到 Observer;
  • ConcreteObserver 状态发生变化,通知各个 ConcreteObserver 并更新;
示例代码
// Subject 接口
interface Subject {
attach(observer: Observer): void;
detach(observer: Observer): void;
notify(): void;
}
// 实现 Subject 接口
class JobAgency implements Subject {
private _observer: Set<Observer>;
private _name: string[];

constructor() {
this._observer = new Set();
this._name = [];
}

attach(observer: Observer): void {
this._observer.add(observer);
}

detach(observer: Observer): void {
this._observer.delete(observer);
}

notify(): void {
for (const observer of this._observer) {
observer.update(this);
}
}

addJob(job: string) {
this._name.push(job);
}

getJob() {
return this._name;
}
}

// Observer 接口
interface Observer {
update(subject: Subject): void;
}
// 实现 Observer 接口
class JobSeek implements Observer {
private _name: string[];

constructor() {
this._name = [];
}

update(subject: JobAgency): void {
this._name = subject.getJob();
}

getJob() {
return this._name;
}
}

const jobAgency = new JobAgency();
const jake = new JobSeek();
const john = new JobSeek();

jobAgency.attach(jake);
jobAgency.attach(john);
console.log(jake.getJob()); // []
console.log(john.getJob()); // []

jobAgency.addJob("worker");
jobAgency.notify();
console.log(jake.getJob()); // [ 'worker' ]
console.log(john.getJob()); // [ 'worker' ]

订阅者

使用场景
  • 一个对象的状态发生改变,依赖于他的对象得到通知并更新;
结构
  • Publisher:发布者;
    • 定义 publish 发布信息至订阅中心;
  • ConcretePublisher:Publisher 接口的具体实现;
  • MessageCenter:订阅中心;
    • 定义 subscribe 接口定义发布者-订阅者关系;
    • 定义 unsubscribe 接口取消发布者-订阅者关系;
    • 定义 publish 接口将制定发布者信息发送到对应订阅者;
  • ConcreteMessageCenter:MessageCenter 接口的最小实现;
  • Subscriber:订阅者接口;
    • 定义 subscribe 接口订阅;
    • 定义 unsubscribe 接口取消订阅;
    • 定义 update 接口更新状态;
  • ConcreteSubscriber:Subscriber 的具体实现;
使用方法
  • Subscriber 通过 MessageCenter 订阅 Publisher;
  • Publisher 通过 MessageCenter 发布消息;
  • MessageCenter 接受消息,并转发至对应 Subscriber;
与观察者模式的不同
  • 观察者模式:观察者知道其对应所有目标;
  • 订阅者模式:发布者不知道其订阅者,通过订阅中心转发至订阅者;
interface SubscribeEvent {
fn: Function;
once: boolean;
}

class EventEmmiter {
subscribes: Map<string, Set<SubscribeEvent>>;

constructor() {
this.subscribes = new Map();
}

// 添加订阅
addEvent(type: string, callback: Function, once: boolean) {
let sub = this.subscribes.get(type);
if (sub) {
sub.add({ fn: callback, once });
} else {
sub = new Set([{ fn: callback, once }]);
this.subscribes.set(type, sub);
}
}

on(type: string, callback: Function) {
this.addEvent(type, callback, false);
}

once(type: string, callback: Function) {
this.addEvent(type, callback, true);
}

// 发布消息
emit(type: string, ...args: Array<any>) {
const sub = this.subscribes.get(type);
if (!sub) return;
for (const event of sub) {
event.fn(...args);
if (event.once) {
sub.delete(event);
}
}
}

// 取消订阅
off(type: string, callback: Function) {
const sub = this.subscribes.get(type);
if (!sub) return;
for (const event of sub) {
if (event.fn !== callback) continue;
sub.delete(event);
}
}
}

const eventEmmiter = new EventEmmiter();

eventEmmiter.once("once", () => {
console.log("once test");
});
eventEmmiter.emit("once");
eventEmmiter.emit("once");

const fn = () => {
console.log("off");
};
eventEmmiter.on("on", fn);
eventEmmiter.on("on", () => {
console.log("no off");
});
eventEmmiter.emit("on");
eventEmmiter.off("on", fn);
eventEmmiter.emit("on");
// once test
// off
// no off
// no off

状态 (STATE)

使用场景
  • 一个对象在其内部状态发生改变时改变其行为;
结构
  • Context:环境,保存 state 实例;
  • State:状态接口,封装与 Context 一个特定状态的对应动作;
  • ConcreteState:实现 State 接口;

状态

使用方法
  • Context 将于状态相关的请求委托到对应 ConcreteState;
示例代码
interface PhoneState {
pickUp(state: PhoneState): PhoneState;
hangUp(state: PhoneState): PhoneState;
dial(state: PhoneState): PhoneState;
}

class PhoneStatePickUp implements PhoneState {
pickUp(state: PhoneState): PhoneState {
console.log("already picked up");
return state;
}
hangUp(state: PhoneState): PhoneState {
return new PhoneStateHangUp();
}
dial(state: PhoneState): PhoneState {
return new PhoneStateDial();
}
}

class PhoneStateHangUp implements PhoneState {
pickUp(state: PhoneState): PhoneState {
return new PhoneStatePickUp();
}
hangUp(state: PhoneState): PhoneState {
console.log("already hang up");
return state;
}
dial(state: PhoneState): PhoneState {
console.log("unable to dial in hang up state");
return state;
}
}

class PhoneStateDial implements PhoneState {
pickUp(state: PhoneState): PhoneState {
console.log("already picked up");
return state;
}
hangUp(state: PhoneState): PhoneState {
return new PhoneStateHangUp();
}
dial(state: PhoneState): PhoneState {
console.log("already dialing");
return state;
}
}

class Phone {
private _state: PhoneState;

constructor() {
this._state = new PhoneStateHangUp();
}

pickUp() {
this._state = this._state.pickUp(this._state);
}
hangUp() {
this._state = this._state.hangUp(this._state);
}
dial() {
this._state = this._state.dial(this._state);
}
}

const phone = new Phone();
phone.dial(); // unable to dial in hang up state
phone.pickUp();
phone.dial();
phone.hangUp();

策略 (STRATEGY)

使用场景
  • 同一种行为有多种实现;
结构
  • Strategy:策略接口;
  • ConcreteStrategy:Strategy 接口的具体实现;
  • Context:上下文;
    • 使用组合将 Strategy 作为其属性;

策略

使用方法
  • 通过工厂方法提供 Strategy;
  • 基于委托依赖注入 Strategy 至 Context;
  • Context 将用户请求转发给 Strategy;
示例代码
// 定义 Strategy 接口
interface SortStrategy {
sort(dataset: number[]): number[];
}
// Strategy 接口的具体实现
class BubbleSortStrategy implements SortStrategy {
sort(dataset: number[]): number[] {
console.log("Sorting using bubble sort");
return dataset.sort((a, b) => a - b);
}
}
class QuickSortStrategy implements SortStrategy {
sort(dataset: number[]): number[] {
console.log("Sorting using quick sort");
return dataset.sort((a, b) => a - b);
}
}

// 定义 Context 接口
class Sorter {
private _strategy: SortStrategy;

constructor(strategy: SortStrategy) {
this._strategy = strategy;
}

sort(dataset: number[]) {
return this._strategy.sort(dataset);
}
}

const smallDataset = [1, 3, 4, 2];
const bigDataset = [1, 4, 3, 2, 8, 10, 5, 6, 9, 7];
const sorter = new Sorter(new BubbleSortStrategy());
// Sorting using bubble sort
// [ 1, 2, 3, 4 ]
console.log(sorter.sort(smallDataset));

const sorter = new Sorter(new QuickSortStrategy());
// Sorting using quick sort
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
console.log(sorter.sort(bigDataset));

模板方法 (TEMPLATE METHOD)

使用场景
  • 抽象类实现不可变部分,定义可变部分接口;
  • 子类继承抽象类,实现可变部分;
结构
  • AbstractClass:定义抽象类;
  • ConcreteClass:定义具体类;

模板方法

使用方法
  • ConcreteClass 实现 AbstractClass 定义的可变部分;
示例代码
// 定义 AbstractClass
abstract class Builder {
// 不可变部分
build() {
this.test();
this.lint();
this.assemble();
this.deploy();
}
// 可变部分
abstract test(): void;
abstract lint(): void;
abstract assemble(): void;
abstract deploy(): void;
}

// 定义具体类
class AndroidBuilder extends Builder {
test() {
console.log("Running android tests");
}

lint() {
console.log("Linting the android code");
}

assemble() {
console.log("Assembling the android build");
}

deploy() {
console.log("Deploying android build to server");
}
}
class IosBuilder extends Builder {
test() {
console.log("Running ios tests");
}

lint() {
console.log("Linting the ios code");
}

assemble() {
console.log("Assembling the ios build");
}

deploy() {
console.log("Deploying ios build to server");
}
}

const android = new AndroidBuilder();
const ios = new IosBuilder();

android.build();
// Running android tests
// Linting the android code
// Assembling the android build
// Deploying android build to server
ios.build();
// Running ios tests
// Linting the ios code
// Assembling the ios build
// Deploying ios build to server

访问者 (VISITOR)

使用场景
  • 对象结构基本不变,但是对其定义新的操作;
结构
  • Visitor:访问者接口;
    • 定义需要被访问的 element;
  • ConcreteVisitor:Visitor 接口的具体实现;
  • Element:元素接口;
    • 定义 accept 操作接受 Visitor;
  • ConcreteElement:Element 的具体实现;
  • ObjectStructure:对象接口;
    • 枚举所有 element;
    • 可不实现;

访问者

使用方法
  • 创建 Visitor,使用 ObjectStructure 访问 Element;
示例代码
// 定义 Element 接口
interface Animal {
accept(operation: AnimalVisitor): void;
}

// 实现 Element 接口
class Monkey implements Animal {
shout() {
console.log("Ooh oo aa aa!");
}

accept(operation: AnimalVisitor): void {
operation.visitMonkey(this);
}
}
class Lion implements Animal {
roar() {
console.log("Roaaar!");
}

accept(operation: AnimalVisitor): void {
operation.visitLion(this);
}
}
class Dolphin implements Animal {
speak() {
console.log("Tuut tuttu tuutt!");
}

accept(operation: AnimalVisitor): void {
operation.visitDolphin(this);
}
}

// 定义 Visitor 接口
interface AnimalVisitor {
visitMonkey(monkey: Monkey): void;
visitLion(lion: Lion): void;
visitDolphin(dolphin: Dolphin): void;
}
// 实现 Visitor 接口
class Speak implements AnimalVisitor {
visitMonkey(monkey: Monkey): void {
monkey.shout();
}
visitLion(lion: Lion): void {
lion.roar();
}
visitDolphin(dolphin: Dolphin): void {
dolphin.speak();
}
}

const monkey = new Monkey();
const lion = new Lion();
const dolphin = new Dolphin();
const speak = new Speak();

monkey.accept(speak); // Ooh oo aa aa!
lion.accept(speak); // Roaaar!
dolphin.accept(speak); // Tuut tuttu tuutt!

最佳实践

自定义 EventEmit