重學設計模式
讀Design pattern implementations in TypeScript筆記。
十一種行為型設計模式
責任鏈模式
namespace ChainOfResponsibilityPattern {
export class Handler {
private handler: Handler;
private req: number;
constructor(req: number) {
this.req = req;
}
public setHandler(handler: Handler): void {
this.handler = handler;
}
public operation(msg: string, req: number): void {
if (req <= this.req) {
this.handlerRequest(msg)
} else if (this.handler !== null && this.handler !== undefined) {
this.handler.operation(msg, req);
}
}
public handlerRequest(msg: string): void {
throw new Error("Abstract method!");
}
}
export class ConcreteHandler1 extends Handler {
constructor(req: number) {
super(req);
}
public handlerRequest(msg: string) {
console.log("Message (ConcreteHandler1) :: ", msg);
}
}
export class ConcreteHandler2 extends Handler {
constructor(req: number) {
super(req);
}
public handlerRequest(msg: string) {
console.log("Message :: (ConcreteHandler2) ", msg);
}
}
export class ConcreteHandler3 extends Handler {
constructor(req: number) {
super(req);
}
public handlerRequest(msg: string) {
console.log("Message :: (ConcreteHandler3) ", msg);
}
}
}
意圖:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關系。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止。
核心:
-
Handler
: 對請求的抽象處理者 -
ConcreteHandler
:請求的具體處理者,他們都繼承了setHandler方法,當自己不處理這個請求的時候,會自動把請求轉移到下一個handler去處理。
命令模式
namespace CommandPattern {
export class Command {
public execute(): void {
throw new Error("Abstract method!");
}
}
export class ConcreteCommand1 extends Command {
private receiver: Receiver;
constructor(receiver: Receiver) {
super();
this.receiver = receiver;
}
public execute(): void {
console.log("`execute` method of ConcreteCommand1 is being called!");
this.receiver.action();
}
}
export class ConcreteCommand2 extends Command {
private receiver: Receiver;
constructor(receiver: Receiver) {
super();
this.receiver = receiver;
}
public execute(): void {
console.log("`execute` method of ConcreteCommand2 is being called!");
this.receiver.action();
}
}
export class Invoker {
private commands: Command[];
constructor() {
this.commands = [];
}
public storeAndExecute(cmd: Command) {
this.commands.push(cmd);
cmd.execute();
}
}
export class Receiver {
public action(): void {
console.log("action is being called!");
}
}
}
核心:
-
Command
:抽象命令,具有執行接口 -
ConcreteCommand
:具體命令,實現具體接口,調用接收者的功能來完成命令要執行的操作。 -
Receiver
:接收者,真正執行命令的對象,任何對象都可以成為接收者,只要實現了對應接口。 -
Invoker
:喚起執行命令的對象,他持有命令對象數組
優點:
- 解耦調用者和執行者
- 可以組合命令
- 可以擴展,對命令進行撤銷等
解釋器模式
namespace InterpreterPattern {
export class Context {
}
export interface AbstractExpression {
interpret(context: Context): void;
}
export class TerminalExpression implements AbstractExpression {
public interpret(context: Context): void {
console.log("`interpret` method of TerminalExpression is being called!");
}
}
export class NonterminalExpression implements AbstractExpression {
public interpret(context: Context): void {
console.log("`interpret` method of NonterminalExpression is being called!");
}
}
}
意圖:給定一個語言,定義它的文法的一種表示,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
核心:
-
AbstractExpression
:抽象表達式,聲明了一個表達式需要實現的接口。 -
TerminalExpression
:實現了抽象表達式角色所要求的接口,主要是一個interpret()方法;文法中的每一個終結符都有一個具體終結表達式與之相對應。比如有一個簡單的公式R=R1+R2,在里面R1和R2就是終結符,對應的解析R1和R2的解釋器就是終結符表達式。 -
NonterminalExpression
:文法中的每一條規則都需要一個具體的非終結符表達式,非終結符表達式一般是文法中的運算符或者其他關鍵字,比如公式R=R1+R2中,“+"就是非終結符,解析“+”的解釋器就是一個非終結符表達式。 -
Context
:上下文,用來存放文法中各個終結符所對應的具體值。
感覺用不上,而且沒有具體例子也比較晦澀(Interpreter Pattern)。
迭代器模式
namespace IteratorPattern {
export interface Iterator {
next(): any;
hasNext(): boolean;
}
export interface Aggregator {
createIterator(): Iterator;
}
export class ConcreteIterator implements Iterator {
private collection: any[] = [];
private position: number = 0;
constructor(collection: any[]) {
this.collection = collection;
}
public next(): any {
// Error handling is left out
var result = this.collection[this.position];
this.position += 1;
return result;
}
public hasNext(): boolean {
return this.position < this.collection.length;
}
}
export class Numbers implements Aggregator {
private collection: number[] = [];
constructor(collection: number[]) {
this.collection = collection;
}
public createIterator(): Iterator {
return new ConcreteIterator(this.collection);
}
}
}
意圖:提供一種方法順序訪問一個聚合對象中各個元素, 而又不需暴露該對象的內部表示。
例子里提供的就是一種正序訪問方法,但實際上這個訪問順序是我們可以自己定義的。
所以迭代器就是一種接口,為各種不同的數據結構提供統一的訪問機制。任何數據結構只要部署 Iterator 接口,就可以完成遍歷操作。也就是可以供es6中的 for...of語法消費。
更多js相關的迭代器知識可以看:Iterator 和 for...of 循環。
中介者模式
namespace MediatorPattern {
export interface Mediator {
send(msg: string, colleague: Colleague): void;
}
export class Colleague {
public mediator: Mediator;
constructor(mediator: Mediator) {
this.mediator = mediator;
}
public send(msg: string): void {
throw new Error("Abstract Method!");
}
public receive(msg: string): void {
throw new Error("Abstract Method!");
}
}
export class ConcreteColleagueA extends Colleague {
constructor(mediator: Mediator) {
super(mediator);
}
public send(msg: string): void {
this.mediator.send(msg, this);
}
public receive(msg: string): void {
console.log(msg, "`receive` of ConcreteColleagueA is being called!");
}
}
export class ConcreteColleagueB extends Colleague {
constructor(mediator: Mediator) {
super(mediator);
}
public send(msg: string): void {
this.mediator.send(msg, this);
}
public receive(msg: string): void {
console.log(msg, "`receive` of ConcreteColleagueB is being called!");
}
}
export class ConcreteMediator implements Mediator {
public concreteColleagueA: ConcreteColleagueA;
public concreteColleagueB: ConcreteColleagueB;
public send(msg: string, colleague: Colleague): void {
if (this.concreteColleagueA === colleague) {
this.concreteColleagueB.receive(msg);
} else {
this.concreteColleagueA.receive(msg);
}
}
}
}
意圖:用一個中介對象來封裝一系列的對象交互。中介者使各對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。
核心:
-
Mediator
:抽象中介者,定義對象間的中介交換方法。 -
ConcreteMediator
:具體中介者,實現對象間的中介交換方法。 -
Colleague
:抽象同事角色,定義同事角色和中介者交互的方法,持有中介者。 -
ConcreteColleagueB
:具體,實現和中介者的交互。
demo:
function show() : void {
var cm: MediatorPattern.ConcreteMediator = new MediatorPattern.ConcreteMediator(),
c1: MediatorPattern.ConcreteColleagueA = new MediatorPattern.ConcreteColleagueA(cm),
c2: MediatorPattern.ConcreteColleagueB = new MediatorPattern.ConcreteColleagueB(cm);
cm.concreteColleagueA = c1;
cm.concreteColleagueB = c2;
c1.send("`send` of ConcreteColleagueA is being called!");
c2.send("`send` of ConcreteColleagueB is being called!");
}
備忘錄模式
namespace MementoPattern {
export class State {
private str: string;
constructor(str: string) {
this.str = str;
}
get Str() : string {
return this.str;
}
set Str(str: string) {
this.str = str;
}
}
export class Originator {
private state: State;
constructor(state: State) {
this.state = state;
}
get State(): State {
return this.state;
}
set State(state: State) {
console.log("State :: ", state);
this.state = state;
}
public createMemento(): Memento {
console.log("creates a memento with a given state!");
return new Memento(this.state);
}
public setMemento(memento: Memento) {
console.log("sets the state back");
this.State = memento.State;
}
}
export class Memento {
private state: State;
constructor (state: State) {
this.state = state;
}
get State(): State {
console.log("get memento's state");
return this.state;
}
}
export class CareTaker {
private memento: Memento;
get Memento(): Memento {
return this.memento;
}
set Memento(memento: Memento) {
this.memento = memento;
}
}
}
意圖:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態。這樣以后就可將該對象恢復到保存的狀態。
核心:
-
Originator
(發起人):負責創建一個備忘錄Memento,用以記錄當前時刻自身的內部狀態,并可使用備忘錄恢復內部狀態。Originator可以根據需要決定Memento存儲自己的哪些內部狀態。 -
Memento
(備忘錄):負責存儲Originator對象的內部狀態,并可以防止Originator以外 的其他對象訪問備忘錄。允許它訪問返回到先前狀態所需要的所有數據。 -
Caretaker
(管理者):管理備忘錄Memento,不能對Memento的內容進行訪問或者操作。
例子:
function show() : void {
var state: MementoPattern.State = new MementoPattern.State("... State "),
originator: MementoPattern.Originator = new MementoPattern.Originator(state),
careTaker: MementoPattern.CareTaker = new MementoPattern.CareTaker();
careTaker.Memento = originator.createMemento();
originator.State = new MementoPattern.State("something else...");
originator.setMemento(careTaker.Memento);
}
以保存游戲進度為例,在游戲角色大戰Boss前將該角色的狀態存儲,與Boss作戰后角色的各項能力會下降,如果沒有通關,則可利用備忘錄進行恢復到戰前狀態。
觀察者模式
namespace ObserverPattern {
export class Subject {
private observers: Observer[] = [];
public register(observer: Observer): void {
console.log(observer, "is pushed!");
this.observers.push(observer);
}
public unregister(observer: Observer): void {
var n: number = this.observers.indexOf(observer);
console.log(observer, "is removed");
this.observers.splice(n, 1);
}
public notify(): void {
console.log("notify all the observers", this.observers);
var i: number
, max: number;
for (i = 0, max = this.observers.length; i < max; i += 1) {
this.observers[i].notify();
}
}
}
export class ConcreteSubject extends Subject {
private subjectState: number;
get SubjectState(): number {
return this.subjectState;
}
set SubjectState(subjectState: number) {
this.subjectState = subjectState;
}
}
export class Observer {
public notify(): void {
throw new Error("Abstract Method!");
}
}
export class ConcreteObserver extends Observer {
private name: string;
private state: number;
private subject: ConcreteSubject;
constructor (subject: ConcreteSubject, name: string) {
super();
console.log("ConcreteObserver", name, "is created!");
this.subject = subject;
this.name = name;
}
public notify(): void {
console.log("ConcreteObserver's notify method");
console.log(this.name, this.state);
this.state = this.subject.SubjectState;
}
get Subject(): ConcreteSubject {
return this.subject;
}
set Subject(subject: ConcreteSubject) {
this.subject = subject;
}
}
}
意圖:定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時, 所有依賴于它的對象都得到通知并被自動更新。
也叫發布訂閱模式,這在前端中非常常見,比如瀏覽器的addEventListener或者node中的EventEmitter,代碼很好理解,不用多做其他解釋。
狀態模式
namespace StatePattern {
export interface State {
handle(context: Context): void;
}
export class ConcreteStateA implements State {
public handle(context: Context): void {
console.log("`handle` method of ConcreteStateA is being called!");
context.State = new ConcreteStateB();
}
}
export class ConcreteStateB implements State {
public handle(context: Context): void {
console.log("`handle` method of ConcreteStateB is being called!");
context.State = new ConcreteStateA();
}
}
export class Context {
private state: State;
constructor(state: State) {
this.state = state;
}
get State(): State {
return this.state;
}
set State(state: State) {
this.state = state;
}
public request(): void {
console.log("request is being called!");
this.state.handle(this);
}
}
}
意圖:允許一個對象在其內部狀態改變時改變它的行為。
適用性: 一個對象的行為取決于它的狀態, 并且它必須在運行時刻根據狀態改變它的行為。
核心:Context
對象包含一個State
狀態對象。而Context
真正執行操作時,是State
狀態對象在執行this.state.handle(this);
。并且當狀態執行完具體操作之后,他會將狀態切換至下一個狀態。這個時候Context
對象再次調用時,它執行的操作會切換至下一個狀態的操作。
例子:
class Kaideng implements State {
public handle(context: Context): void {
console.log("關燈");
context.State = new Guandeng();
}
}
class Guandeng implements State {
public handle(context: Context): void {
console.log("開燈");
context.State = new Kaideng();
}
}
class Light {
private state: State;
constructor(state: State) {
this.state = state;
}
get State(): State {
return this.state;
}
set State(state: State) {
this.state = state;
}
public toggle(): void {
console.log("request is being called!");
this.state.handle(this);
}
}
const light = new Light(new Guandeng());
light.toggle();
light.toggle();
策略模式
namespace StrategyPattern {
export interface Strategy {
execute(): void;
}
export class ConcreteStrategy1 implements Strategy {
public execute(): void {
console.log("`execute` method of ConcreteStrategy1 is being called");
}
}
export class ConcreteStrategy2 implements Strategy {
public execute(): void {
console.log("`execute` method of ConcreteStrategy2 is being called");
}
}
export class ConcreteStrategy3 implements Strategy {
public execute(): void {
console.log("`execute` method of ConcreteStrategy3 is being called");
}
}
export class Context {
private strategy: Strategy;
constructor(strategy: Strategy) {
this.strategy = strategy;
}
public executeStrategy(): void {
this.strategy.execute();
}
}
}
意圖:定義一系列的算法,把它們一個個封裝起來, 并且使它們可相互替換。本模式使得算法可獨立于使用它的客戶而變化。
核心:
“策略”提供了一種用多個行為中的一個行為來配置一個類的方法。
模版方法模式
namespace TemplateMethodPattern {
export class AbstractClass {
public method1(): void {
throw new Error("Abstract Method");
}
public method2(): void {
throw new Error("Abstract Method");
}
public method3(): void {
throw new Error("Abstract Method");
}
public templateMethod(): void {
console.log("templateMethod is being called");
this.method1();
this.method2();
this.method3();
}
}
export class ConcreteClass1 extends AbstractClass {
public method1(): void {
console.log("method1 of ConcreteClass1");
}
public method2(): void {
console.log("method2 of ConcreteClass1");
}
public method3(): void {
console.log("method3 of ConcreteClass1");
}
}
export class ConcreteClass2 extends AbstractClass {
public method1(): void {
console.log("method1 of ConcreteClass2");
}
public method2(): void {
console.log("method2 of ConcreteClass2");
}
public method3(): void {
console.log("method3 of ConcreteClass2");
}
}
}
核心:定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。Template Method使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
優點:封裝不變部分,擴展可變部分。
訪問者模式
namespace VisitorPattern {
export interface Visitor {
visitConcreteElement1(concreteElement1: ConcreteElement1): void;
visitConcreteElement2(concreteElement2: ConcreteElement2): void;
}
export class ConcreteVisitor1 implements Visitor {
public visitConcreteElement1(concreteElement1: ConcreteElement1): void {
console.log("`visitConcreteElement1` of ConcreteVisitor1 is being called!");
}
public visitConcreteElement2(concreteElement2: ConcreteElement2): void {
console.log("`visitConcreteElement2` of ConcreteVisitor1 is being called!");
}
}
export class ConcreteVisitor2 implements Visitor {
public visitConcreteElement1(concreteElement1: ConcreteElement1): void {
console.log("`visitConcreteElement1` of ConcreteVisitor2 is being called!");
}
public visitConcreteElement2(concreteElement2: ConcreteElement2): void {
console.log("`visitConcreteElement2` of ConcreteVisitor2 is being called!");
}
}
export interface Element {
operate(visitor: Visitor): void;
}
export class ConcreteElement1 implements Element {
public operate(visitor: Visitor): void {
console.log("`operate` of ConcreteElement1 is being called!");
visitor.visitConcreteElement1(this);
}
}
export class ConcreteElement2 implements Element {
public operate(visitor: Visitor): void {
console.log("`operate` of ConcreteElement2 is being called!");
visitor.visitConcreteElement2(this);
}
}
export class Objs {
private elements: Element[] = [];
public attach(e: Element): void {
this.elements.push(e);
}
public detach(e: Element): void {
var index = this.elements.indexOf(e);
this.elements.splice(index, 1);
}
public operate(visitor: Visitor): void {
var i = 0,
max = this.elements.length;
for(; i < max; i += 1) {
this.elements[i].operate(visitor);
}
}
}
}
意圖:表示一個作用于某對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作。
核心:
-
Visitor
:為該對象結構中 ConcreteElement的每一個類聲明一個 Visit操作。 -
ConcreteVisitor
:實現每個由 Visitor聲明的操作。 -
ConcreteElement
:實現operate方法,該操作以一個訪問者為參數,然后具體地調用訪問者的方法。 -
ObjectStructure
:對應Objs
,可以對元素進行管理,枚舉元素等。
簡記:不同的人對不同的事做不同的操作。
為什么要重學設計模式?
不同的階段看設計模式會有不同的感悟,并且有很多設計模式即使然在懂了也不是能馬上運用在項目中的,所以需要不停地反復學習,才會有更熟練的感覺。