前端工程中的设计模式应用

8bee58143225525314ba9af0a5e0ae18.gif

本文旨在系统性介绍一下23种设计模式,给出通俗易懂的案例、结构图及代码示例,这也是我自身学习理解的过程。或许其中的几种设计模式写的并不是很清晰明了易懂,更详细的可根据提到的参考文献进行深入学习。

37b0bf9d277c01ef0b7955755d129514.png

什么是设计模式

设计模式这个概念是由一本名为《设计模式:可复用面向对象软件的基础》的书推广而来,这本书在1994年由四个C++工程师编写的。这本书探讨了面向对象的编程的能力和陷阱,并介绍了23种可以用来解决编程问题的模式。这些模式并不是算法或者具体的实现,它们更像是想法、观点和抽象,辅助我们去解决一些特定问题。但是这些模式建立在C++的OOP的基础之上,当使用更现代的编程语言如JavaScript时,模式可能不等效,甚至给代码添加了不必要的限制,但是我们仍然可以把这些模式当做一些编程知识来学习了解。我们学习使用这些模式的目的是实现代码的高内聚和低耦合,提高代码的复用性,让代码更容易的被他人理解并保证代码的可靠性。

9a3e02b804a085218ac3284b7ebd786d.png

23种设计模式简介

  创建型(Creational)(5种)

提供创建对象的机制, 增加已有代码的灵活性和可复用性。

  • 单例模式(Singleton)

保证一个类只有一个实例, 并提供一个访问该实例的全局节点。

  • 原型模式(Prototype)

允许把一个对象作为蓝图创建另一个对象,新对象继承原对象的属性和方法。

  • 生成器模式(Builder)

分“步骤”创建对象。

  • 工厂方法模式(Factory Method)

提供创建对象的接口,对象被创建后可以修改。

  • 抽象工厂模式(Abstract Factory)

允许在不指定具体类的情况下生成一系列相关的对象。

  结构型(Structural)(7种)

介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。

  • 桥接模式(Bridge)

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

  • 外观模式(Facade)

给库、框架以及其他复杂的类集提供简化的接口。

  • 组合模式(Composite)

将对象组合成树形结构来表现整体和部分的层次结构

  • 装饰器模式(Decorator)

通过增加一个修饰对象来包裹原来的对象,从而给原来的对象添加新的行为

  • 适配器模式(Adapter)

允许两个接口不兼容的对象相互交互

  • 代理模式(Proxy)

为另一个对象提供替代或者占位符

  • 享元模式(Flyweight)

运用共享技术来有效地支持大量细粒度对象的复用,以减少创建的对象的数量

  行为型(Behavioral)(11种)

负责对象间的高效沟通和职责委派。

  • 迭代器模式(Iterator)

用于遍历集合的元素

  • 解释器模式(Interpreter )

给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子

  • 观察者模式(Observer)

定义一个订阅机制来通知多个对象它们在观察的对象发生的任何事件

  • 中介者模式(Mediator)

用一个中介对象来封装多个对象之间的复杂交互

  • 访问者模式(Visitor)

针对于对象结构中的元素,定义在不改变该对象的前提下访问其结构中元素的新方法。

  • 状态模式(State)

当一个对象的内部状态发生改变时,会导致其行为的改变,这看起来像是改变了对象

  • 备忘录模式(Memento)

允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。

  • 策略模式(Strategy)

定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

  • 模板方法模式(Template Method)

父类中定义一组操作算法骨架,而将一些实现步骤延迟到子类中,使得子类可以不改变父类的算法结构的同时,重新定义算法中的某些实现步骤。

  • 职责链模式(Chain of Responsibility)

将请求通过处理链传递,链条上的每一个处理程序决定要么处理请求,要么将请求传递给链条上的下一个处理程序。

  • 命令模式(Command)

将请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

704a6b253ee2197c5726ade860ab60d8.png

设计模式细解

  创建型

创建型模式提供了创建对象的机制, 能够提升已有代码的灵活性和可复用性。

  • 单例模式

单例模式是一种创建型设计模式,它保证一个类只有一个实例, 并提供一个访问该实例的全局节点。

举个现实的例子,比如一个国家只有一个官方政府。不管组成政府的每个人的身份是什么, “某政府” 这一称谓总是鉴别那些掌权者的全局访问节点。

单例模式结构:

6c639eea863988c9f1dc9f78a0ed6301.png

代码实现:

class Singleton {constructor() {this.instance = null; // 单例模式的标识 }getInstance() {if (!this.instance) {this.instance = new Singleton(name);}return this.instance;}
}function clientCode() {const s1 = Singleton.getInstance();const s2 = Singleton.getInstance();if (s1 === s2) {console.log('同一个实例');} else {console.log('不同实例');}
}clientCode(); // 同一个实例
  • 原型模式

原型模式一种创建型设计模式, 它能够复制已有对象,获取原有对象的属性和方法,而又无需使代码依赖它们所属的类。

举个现实的例子,比如细胞的有丝分裂。有丝分裂会产生一对完全相同的细胞。原始细胞就是一个原型, 它在复制体的生成过程中起到了推动作用。

原型模式结构:

9eb2908942ddf5b86d49a56b9e1b98e1.png

代码实现:

class Prototype {constructor(name) {this.name = name;}setName(name) {this.name = name;}clone() { return new Prototype(this.name);}
}function clientCode() {const p1 = new Prototype('原型模式');const p2 = p1.clone();if(p1.name === p2.name) {console.log('属性复制成功');}p2.setName('复制的原型模式');if(p2.name === '复制的原型模式') {console.log('方法复制成功,并不依赖于原先的类');}}clientCode(); // 属性复制成功   方法复制成功,并不依赖于原先的类
  • 生成器模式

构造器模式是一种创建型设计模式, 它能够分步骤创建复杂对象,可以使用相同的创建代码生成不同类型和形式的对象。

举个现实的例子,比如汽车的制造,A工厂生产轮胎,B工厂生产车门,C工厂生产玻璃,...  最终Z工厂负责组装这些零件,出厂一辆完整的汽车。

生成器模式结构:

7be2a3da082b4f0f3e2668dd0709b3ee.png

简而言之,生成器模式可以概括为两部分,第一部分是具体生成器(ConcreteBuilder)负责new Product,并提供处理Product的各种方法;第二部分主管(Director)负责处理Product的各种方法的调用。

代码实现:

interface Builder {  // 指定创建product对象不同部分的方法producePartA(): void;producePartB(): void;producePartC(): void;
}
// 要创建的产品
class Product {public parts: string[] = [];public listParts(): void {console.log(`Product parts: ${this.parts.join(', ')}\n`);}
}
// 具体生成器1
class ConcreteBuilder implements Builder {private product: Product1; // 空的产品对象,用于后续的组装constructor() {this.reset();}public reset(): void {this.product = new Product();}
// 所有的生产步骤都是对同一个product实例进行操作public producePartA(): void {this.product.parts.push('PartA1');}public producePartB(): void {this.product.parts.push('PartB1');}public producePartC(): void {this.product.parts.push('PartC1');}
// 获取产品的方法public getProduct(): Product {const result = this.product;this.reset(); // 调用重置方法,做好生产下一个产品的准备,但这并不是必须的return result;}
}
// 定义创建步骤的执行顺序, 其中生成器负责提供这些步骤的实现。
class Director {private builder: Builder;public setBuilder(builder: Builder): void {this.builder = builder;}public buildMinimalViableProduct(): void {this.builder.producePartA();}public buildFullFeaturedProduct(): void {this.builder.producePartA();this.builder.producePartB();this.builder.producePartC();}
}
// 客户端代码
function clientCode(director: Director) {const builder = new ConcreteBuilder(); director.setBuilder(builder); // 将主管类与具体生成器进行关联console.log('生成一个基础产品:');director.buildMinimalViableProduct();builder.getProduct().listParts(); // Product parts: PartA1console.log('生成一个完整产品:');director.buildFullFeaturedProduct();builder.getProduct().listParts(); // Product parts: PartA1, PartB1, PartC1// builder类也可以不依赖于Director,可以直接调用其内部的方法console.log('生成其他定制产品:');builder.producePartA();builder.producePartC();builder.getProduct().listParts(); // Product parts: PartA1, PartC1
}const director = new Director();
clientCode(director);
  • 工厂方法模式

工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。

工厂方法模式结构:

138aaa32f1d8c2b67441dc6ff6fc64c3.png

代码实现:

interface Product { // 声明了所有产品必须实现的操作operation(): string;
}
// 创建者类,声明返回产品对象的工厂方法
abstract class Creator {public abstract factoryMethod(): Product;public someOperation(): string {const product = this.factoryMethod(); // 调用工厂方法创建一个产品对象return `Creator: 同一个Creator的代码被应用在 ${product.operation()}`;}
}
// 具体创建者将重写工厂方法,以改变其所返回的产品类型
class ConcreteCreator1 extends Creator {public factoryMethod(): Product {return new ConcreteProduct1();}
}
class ConcreteCreator2 extends Creator {public factoryMethod(): Product {return new ConcreteProduct2();}
}
// 具体产品需提供产品接口的各种实现
class ConcreteProduct1 implements Product {public operation(): string {return '{ConcreteProduct1的结果}';}
}
class ConcreteProduct2 implements Product {public operation(): string {return '{ConcreteProduct2的结果}';}
}
// 客户端代码
function clientCode(creator: Creator) {console.log(creator.someOperation());
}console.log('App: ConcreteCreator1 启动');
clientCode(new ConcreteCreator1());
//Creator: 同一个Creator的代码被应用在 {ConcreteProduct1的结果}console.log('App: Launched with the ConcreteCreator2.');
clientCode(new ConcreteCreator2()); 
//Client: I'm not aware of the creator's class, but it still works.
// Creator: 同一个Creator的代码被应用在 {ConcreteProduct2的结果}

简而言之,客户端传入的是具体生成器,然后创建出一个新商品,不同的具体生成器可以产出不同的具体商品。也就是父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。

  • 抽象工厂模式

抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。

抽象工厂模式结构:

7e8064ea63bfb8f5dae2441ba7fe23da.png

代码实现:

// 抽象工厂接口,声明可返回不同抽象产品的方法
interface AbstractFactory {createProductA(): AbstractProductA;createProductB(): AbstractProductB;
}
// 抽象产品接口
interface AbstractProductA {usefulFunctionA(): string;
}
interface AbstractProductB {usefulFunctionB(): string;anotherUsefulFunctionB(collaborator: AbstractProductA): string;
}
// 具体工厂 可生成属于同一变体的系列产品
class ConcreteFactory1 implements AbstractFactory {public createProductA(): AbstractProductA {return new ConcreteProductA1();}public createProductB(): AbstractProductB {return new ConcreteProductB1();}
}
class ConcreteFactory2 implements AbstractFactory {public createProductA(): AbstractProductA {return new ConcreteProductA2();}public createProductB(): AbstractProductB {return new ConcreteProductB2();}
}
// 具体产品 由相应的具体工厂创建
class ConcreteProductA1 implements AbstractProductA {public usefulFunctionA(): string {return '这里是A1产品';}
}
class ConcreteProductA2 implements AbstractProductA {public usefulFunctionA(): string {return '这里是A2产品';}
}
class ConcreteProductB1 implements AbstractProductB {public usefulFunctionB(): string {return '这里是B1产品';}public anotherUsefulFunctionB(collaborator: AbstractProductA): string {const result = collaborator.usefulFunctionA();return `这里是B1产品合作 (${result})`;}
}
class ConcreteProductB2 implements AbstractProductB {public usefulFunctionB(): string {return '这里是B2产品';}public anotherUsefulFunctionB(collaborator: AbstractProductA): string {const result = collaborator.usefulFunctionA();return `这里是B2产品合作 (${result})`;}
}
// 客户端代码 仅通过抽象类型AbstractFactory使用工厂和产品
function clientCode(factory: AbstractFactory) {const productA = factory.createProductA();const productB = factory.createProductB();console.log(productB.usefulFunctionB());console.log(productB.anotherUsefulFunctionB(productA));
}
clientCode(new ConcreteFactory1()); // 这里是B1产品 // 这里是B1产品合作(A1)
clientCode(new ConcreteFactory2()); // 这里是B2产品 // 这里是B2产品合作(A2)

简而言之,客户端的入参是具体工厂(具体工厂是抽象工厂的实现),具体工厂会生成具体产品。抽象工厂为你提供了一个接口, 可用于创建每个系列产品的对象。只要代码通过该接口创建对象, 那么你就不会生成与应用程序已生成的产品类型不一致的产品。

  结构型

结构型模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。

  • 桥接模式

桥接模式是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为不同的层次结构, 从而能在开发时分别使用。

桥接模式结构:

ed16dbb3fabab2c932968ac3f33694c6.png

代码实现:

// 定义了所有Implementation类的接口
interface Implementation {operationImplementation(): string;
}
class Abstraction {// 提供对Implementation的引用,并将所有的实际工作委托给它protected implementation: Implementation;constructor(implementation: Implementation) {this.implementation = implementation;}public operation(): string {const result = this.implementation.operationImplementation();return `抽象部分: 基础操作: ${result}`;}
}
// 在不改变Implementation类的前提下继承Abstraction
class ExtendedAbstraction extends Abstraction {public operation(): string {const result = this.implementation.operationImplementation();return `继承的抽象部分: 继承的操作:${result}`;}
}
class ConcreteImplementationA implements Implementation {public operationImplementation(): string {return '具体实现A: 这里是A的结果';}
}
class ConcreteImplementationB implements Implementation {public operationImplementation(): string {return '具体实现B: 这里是B的结果';}
}
// 客户端代码
function clientCode(abstraction: Abstraction) {console.log(abstraction.operation());
}let implementation = new ConcreteImplementationA();
let abstraction = new Abstraction(implementation);
clientCode(abstraction); // 抽象部分:基础操作:具体实现A: 这里是A的结果.
implementation = new ConcreteImplementationB();
abstraction = new ExtendedAbstraction(implementation);
clientCode(abstraction); // 继承的抽象部分: 继承的操作: 具体实现B: 这里是B的结果.

简单来说,对于一个复杂庞大的系统,可以将其分层,第一层是抽象层,第二层为具体层,抽象层可以把对自己的调用委派给实现层实现,而所有的实现层都具有统一接口,所以实现层可以在抽象部分内部进行相互替换。

  • 外观模式

外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。

举个现实的例子,淘宝就是一个外观,它提供了你购物、支付、送花上门等接口。

外观模式结构:

99ee04a195ad20e1bc8ac3581e9d5e77.png

代码实现:

// 为一个或多个子系统的复杂逻辑提供简单接口
class Facade {protected subsystem1: Subsystem1;protected subsystem2: Subsystem2;constructor(subsystem1?: Subsystem1, subsystem2?: Subsystem2) {this.subsystem1 = subsystem1 || new Subsystem1();this.subsystem2 = subsystem2 || new Subsystem2();}public operation(): string {let result = 'Facade初始化子系统';result += this.subsystem1.operation1();result += this.subsystem2.operation1();result += 'Facade命令子系统执行操作';result += this.subsystem1.operationN();result += this.subsystem2.operationZ();return result;}
}
class Subsystem1 {public operation1(): string {return 'Subsystem1准备好了!';}public operationN(): string {return 'Subsystem1执行!';}
}
class Subsystem2 {public operation1(): string {return 'Subsystem2准备好了';}public operationZ(): string {return 'Subsystem2执行!';}
}
// 客户端代码
function clientCode(facade: Facade) {console.log(facade.operation());
}
const subsystem1 = new Subsystem1();
const subsystem2 = new Subsystem2();
const facade = new Facade(subsystem1, subsystem2);
clientCode(facade); // Facade初始化子系统 Subsystem1准备好了! Subsystem2准备好了 Facade命令子系统执行操作 Subsystem1执行! Subsystem2执行!
  • 组合模式

组合模式是一种结构型设计模式, 你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们。

举个现实的例子,比如学校,学校由年级组成,年级由班级组成,班级由学生个体组成。当学校下达重要通知时,是逐级下发的,通过一个个层级的传递,直到每个学生都接收到通知。

组合模式结构:

9a5c715c224df1d5b1ef90fad7e829e4.png

代码示例:

// 描述了 简单 和 复杂 所共有的操作
abstract class Component {protected parent!: Component | null;public setParent(parent: Component | null) {this.parent = parent;}public getParent(): Component | null {return this.parent;}public add(component: Component): void { }public remove(component: Component): void { }public isComposite(): boolean {return false;}public abstract operation(): string;
}
// 叶子 执行具体的工作,不再包含子项目
class Leaf extends Component {public operation(): string {return 'Leaf';}
}
// 容器 将具体工作委托给子项目,然后汇总结果
class Composite extends Component {protected children: Component[] = [];public add(component: Component): void {this.children.push(component);component.setParent(this);}public remove(component: Component): void {const componentIndex = this.children.indexOf(component);this.children.splice(componentIndex, 1);component.setParent(null);}public isComposite(): boolean {return true;}public operation(): string {const results = [];for (const child of this.children) {results.push(child.operation());}return `Branch(${results.join('+')})`;}
}
// 客户端 通过基础接口与所有的组件链接
function clientCode(component: Component) {console.log(`RESULT: ${component.operation()}`);
}
const simple = new Leaf();
console.log('Client: 简单的:');
clientCode(simple); // RESULT: Leafconst tree = new Composite();
const branch1 = new Composite();
branch1.add(new Leaf());
branch1.add(new Leaf());
const branch2 = new Composite();
branch2.add(new Leaf());
tree.add(branch1);
tree.add(branch2);
console.log('Client: 复杂的:');
clientCode(tree); // RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf))function clientCode2(component1: Component, component2: Component) {if (component1.isComposite()) {component1.add(component2);}console.log(`RESULT: ${component1.operation()}`);
}
console.log('Client: 当在管理树状结构时,不需要检查组件类');
clientCode2(tree, simple); // RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf)+Leaf)

简单来说,组合模式最主要的功能是在整个树状结构上递归调用方法并对结果进行汇总。

  • 装饰器模式

装饰模式是一种结构型设计模式, 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。

举个现实的例子,比如天冷加衣服,觉得冷了可以加一件毛衣,还觉得冷可以再加一件羽绒服。这些衣物扩展了你的基本行为,但他们并不是你的一部分,如果不再需要了,可以随时脱掉。如果下雨了,还可以随时再加一件雨衣。

装饰器模式结构:

26733854fdb9ab50e22e8e1e579f1717.png

代码示例:

interface Component { // 定义了可被装饰器修改的操作operation(): string;
}
// 具体部件提供了操作的默认实现 但是装饰类可以改变这些操作
class ConcreteComponent implements Component {public operation(): string {return 'ConcreteComponent';}
}
// 
class Decorator implements Component {protected component: Component;constructor(component: Component) {this.component = component;}public operation(): string {return this.component.operation();}
}
class ConcreteDecoratorA extends Decorator {public operation(): string {return `ConcreteDecoratorA(${super.operation()})`;}
}
class ConcreteDecoratorB extends Decorator {public operation(): string {return `ConcreteDecoratorB(${super.operation()})`;}
}
// 客户端
function clientCode(component: Component) {console.log(`RESULT: ${component.operation()}`);
}
const simple = new ConcreteComponent();
console.log('Client: 简单的部件:');
clientCode(simple); // RESULT: ConcreteComponentconst decorator1 = new ConcreteDecoratorA(simple);
const decorator2 = new ConcreteDecoratorB(decorator1);
console.log('Client: 装饰器部件:');
clientCode(decorator2); // RESULT: ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))

简答来说,目标对象和装饰器是遵循同一接口的,因此可以使用装饰对对象进行无限次的封装,结果对象将获得所有装饰器装饰后的叠加结果。

  • 适配器模式

适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。

举个现实的例子,电源适配器。

适配器模式结构:

4acd4ee471a6c056e8eaeea9d594fb9f.png

代码示例:

class Target { // 目标public request(): string {return 'Target: The default target\'s behavior.';}
}
class Adaptee { // 被适配者public specificRequest(): string {return '.eetpadA eht fo roivaheb laicepS';}
}
class Adapter extends Target {private adaptee: Adaptee;constructor(adaptee: Adaptee) {super();this.adaptee = adaptee;}public request(): string {const result = this.adaptee.specificRequest().split('').reverse().join('');return `Adapter: (TRANSLATED) ${result}`;}
} 
// 客户端 支持所有遵循Target接口的类
function clientCode(target: Target) {console.log(target.request());
}
const target = new Target();
clientCode(target); // Target: 这是默认的目标行为.const adaptee = new Adaptee();
console.log(`Adaptee: ${adaptee.specificRequest()}`); // Adaptee: .eetpadA eht fo roivaheb laicepSconst adapter = new Adapter(adaptee);
clientCode(adapter); // Adapter: (TRANSLATED) Special behavior of the Adaptee.

简而言之,适配器接收对一个对象的调用,并将其转换为另一个对象可识别的格式和接口。


  • 代理模式

代理模式是一种结构型设计模式, 它能够提供对象的替代品或占位符。代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

举个现实的例子,我们的数字支付工具比如支付宝支付、微信支付就是现金的代理,他们都实现了同一的接口,均可以用于支付。消费者和被消费者都会非常的满意,因为双方都很便捷。

代理模式结构:

4e49418d0de9f9aab6eb8f631e92a214.png

代码示例:

interface ServiceInterface { // 声明服务接口,实际服务者和代理者要遵循相同的接口request(): void;
}
// 实际服务者
class Service implements ServiceInterface {public request(): void {console.log('实际服务者: 处理请求');}
}
// 代理者
class Proxy implements ServiceInterface {private service: Service;// 代理会维护一个对实际服务者的引用constructor(service: Service) {this.service = service;}public request(): void {if (this.checkAccess()) {this.service.request();this.logAccess();}}private checkAccess(): boolean {// 在这里执行一些处理console.log('代理:触发真正的请求之前进行检查访问');return true;}private logAccess(): void {console.log('代理:请求之后的一些处理');}
}
// 客户端代码
function clientCode(serviceInterface: ServiceInterface) {serviceInterface.request();
}
// 客户端直接调用实际服务者
const service = new Service(); 
clientCode(service); // 实际服务者: 处理请求.
// 客户端通过代理调用实际服务者
const proxy = new Proxy(service);
clientCode(proxy); // 代理:触发真正的请求之前进行检查访问. 实际服务者: 处理请求. 代理:请求之后的一些处理.
  • 享元模式

享元模式是一种结构型设计模式, 它摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象。

享受元模式结构:

3acd87fbb44072a6e8f79fd682b1d6f0.png

代码示例:

// 存储共有状态,接收其余状态
class Flyweight {private sharedState: any;constructor(sharedState: any) {this.sharedState = sharedState;}public operation(uniqueState): void {const s = JSON.stringify(this.sharedState);const u = JSON.stringify(uniqueState);console.log(`享元: 共享 (${s}),独有 (${u})`);}
}
// 创建和管理Flyweight对象
class FlyweightFactory {private flyweights: {[key: string]: Flyweight} = <any>{};constructor(initialFlyweights: string[][]) {for (const state of initialFlyweights) {this.flyweights[this.getKey(state)] = new Flyweight(state);}}private getKey(state: string[]): string {return state.join('_');}public getFlyweight(sharedState: string[]): Flyweight {const key = this.getKey(sharedState);if (!(key in this.flyweights)) {console.log('享元工厂: 不能够寻找到享元,创建一个新的');this.flyweights[key] = new Flyweight(sharedState);} else {console.log('享元工厂: 找到了已存在的享元');}return this.flyweights[key];}public listFlyweights(): void {const count = Object.keys(this.flyweights).length;console.log(`享元工厂: 我有 ${count} 个享元:`);for (const key in this.flyweights) {console.log(key);}}
}
// 客户端代码  先创建一些预先填充的flyweight
const factory = new FlyweightFactory([['Chevrolet', 'Camaro2018', 'pink'],['Mercedes Benz', 'C300', 'black'],['Mercedes Benz', 'C500', 'red'],['BMW', 'M5', 'red'],['BMW', 'X6', 'white'],
]);
factory.listFlyweights(); // 享元工厂: 我有5个享元:
// Chevrolet-Camaro2018-pink
// Mercedes Benz-C300-black
// Mercedes Benz-C500-red
// BMW-M5-red
// BMW-X6-white
function addCarToPoliceDatabase(ff: FlyweightFactory, plates: string, owner: string,brand: string, model: string, color: string,
) {const flyweight = ff.getFlyweight([brand, model, color]);flyweight.operation([plates, owner]);
}addCarToPoliceDatabase(factory, 'CL234IR', 'James Doe', 'BMW', 'M5', 'red'); 
// 享元工厂: 找到了已存在的享元.
// 享元: 共享 (['BMW', 'M5', 'red']), 独有 (['CL234IR', 'James Doe']).
addCarToPoliceDatabase(factory, 'CL234IR', 'James Doe', 'BMW', 'X1', 'red');
// 享元工厂: 不能够寻找到享元,创建一个新的
// 享元: 共享 (['BMW', 'X1', 'red']), 独有 (['CL234IR', 'James Doe']) state.
factory.listFlyweights(); // 享元工厂: 我有6个享元:
// Chevrolet-Camaro2018-pink
// Mercedes Benz-C300-black
// Mercedes Benz-C500-red
// BMW-M5-red
// BMW-X6-white
// BMW-X1-red

简单来说,享元模式通过共享多个对象的部分状态来实现上述功能,享元会将不同对象的相同数据进行缓存以节省内存。如同上述例子中,车的型号会被存储在享元中,车的主人是独享的。当将一个 主人-车辆 数据加入数据库中时,享元工厂先查找享元中是否已存在该型号车的数据,有的话直接返回,没有的话创建这个型号。

  行为型

行为模式负责对象间的高效沟通和职责委派。

  • 迭代器模式

迭代器模式是一种行为设计模式, 它能够在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。

举个现实的例子,假如去北京旅游,故宫、长城、天安门、颐和园、北大、清华是此次旅行的游览目标,可以将他们组成一个目的地集合。第一种游览方式是按照自己的意愿顺序去游玩,第二种方式是按照某些博主推荐的游览顺序游玩,第三种方式是报个旅行团,按照旅行团安排的顺序游玩。以上这三种选择就是目的地集合的迭代器。

迭代器模式结构:

8e246b51009d3ea7f97c601bf05d3d2d.png

代码示例:

interface Iterator<T> {current(): T; // 返回当前的元素next(): T; // 返回下一个元素key(): number; // 返回当前元素的keyvalid(): boolean; // 检测当前位置是否是有效的rewind(): void; // 将迭代器回退到第一个元素
}
interface Aggregator {getIterator(): Iterator<string>; // 获取外部迭代器
}
// 具体迭代器实现各种遍历算法。这些类在任何时候都存储当前遍历位置
class AlphabeticalOrderIterator implements Iterator<string> {private collection: WordsCollection;private position: number = 0;private reverse: boolean = false;constructor(collection: WordsCollection, reverse: boolean = false) {this.collection = collection;this.reverse = reverse;if (reverse) {this.position = collection.getCount() - 1;}}public rewind() {this.position = this.reverse ?this.collection.getCount() - 1 :0;}public current(): string {return this.collection.getItems()[this.position];}public key(): number {return this.position;}public next(): string {const item = this.collection.getItems()[this.position];this.position += this.reverse ? -1 : 1;return item;}public valid(): boolean {if (this.reverse) {return this.position >= 0;}return this.position < this.collection.getCount();}
}
// 具体集合提供一个或多个方法来检索新的迭代器实例,与集合类兼容。
class WordsCollection implements Aggregator {private items: string[] = [];public getItems(): string[] {return this.items;}public getCount(): number {return this.items.length;}public addItem(item: string): void {this.items.push(item);}public getIterator(): Iterator<string> {return new AlphabeticalOrderIterator(this);}public getReverseIterator(): Iterator<string> {return new AlphabeticalOrderIterator(this, true);}
}
// 客户端代码
const collection = new WordsCollection();
collection.addItem('First');
collection.addItem('Second');
collection.addItem('Third');
const iterator = collection.getIterator();
console.log('Straight traversal:');
while (iterator.valid()) {console.log(iterator.next()); // First Second Third
}
const reverseIterator = collection.getReverseIterator();
while (reverseIterator.valid()) {console.log(reverseIterator.next()); // Third Second First
}

最简单的理解方式的话,其实就是我们常说的ES6中的迭代器。

  • 解释器模式

解释器模式是一种行为设计模式。给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。

代码示例:

class Context {constructor() {this._list = []; // 存放 终结符表达式this._sum = 0; // 存放 非终结符表达式(运算结果)}get sum() {return this._sum;}set sum(newValue) {this._sum = newValue;}add(expression) {this._list.push(expression);}get list() {return [...this._list];}
}
class PlusExpression {interpret(context) {if (!(context instanceof Context)) {throw new Error("TypeError");}context.sum = ++context.sum;}
}
class MinusExpression {interpret(context) {if (!(context instanceof Context)) {throw new Error("TypeError");}context.sum = --context.sum;}
}
// 客户端代码
const context = new Context();
// 添加加法表达式
context.add(new PlusExpression()); 
// 添加加法表达式
context.add(new PlusExpression());
// 添加减法表达式
context.add(new MinusExpression());
// 依次执行: 加法、加法、减法表达式
context.list.forEach(expression => expression.interpret(context));
console.log(context.sum); // 1
  • 观察者模式

观察者模式是一种行为设计模式,又被称为发布订阅模式。它允许定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。

举个现实的例子,比如你订阅了人民日报,每当它发版的时候,都会有邮递员将报纸送到你手中,而不需要你跑去报刊亭自己购买。出版社为发布者,你为订阅者,出版社维护着一份订阅者的名单,当你不想再订阅时可以申请退出订阅。

观察者模式结构:

c04b441d2b33f672bacd26945383f5b5.png

代码示例:

interface Subject { // 声明了一组管理订阅者的方法attach(observer: Observer): void; // 为订阅者附加观察者detach(observer: Observer): void; // 从订阅者身上剥离观察者notify(): void; // 通知所有观察者的方法
}
// 具体订阅者
class ConcreteSubject implements Subject { public state: number;private observers: Observer[] = [];public attach(observer: Observer): void {const isExist = this.observers.includes(observer);if (isExist) {return console.log('Subject: Observer has been attached already.');}console.log('Subject: Attached an observer.');this.observers.push(observer);}public detach(observer: Observer): void {const observerIndex = this.observers.indexOf(observer);if (observerIndex === -1) {return console.log('Subject: Nonexistent observer.');}this.observers.splice(observerIndex, 1);console.log('Subject: Detached an observer.');}// 触发每个订阅者更新public notify(): void {console.log('Subject: Notifying observers...');for (const observer of this.observers) {observer.update(this);}}// 业务逻辑等,当其发生变化时,触发notify方法public someBusinessLogic(): void {console.log('\nSubject: I\'m doing something important.');this.state = Math.floor(Math.random() * (10 + 1));console.log(`Subject: My state has just changed to: ${this.state}`);this.notify();}
}
interface Observer { // 声明更新方法,由订阅者调用// Receive update from subject.update(subject: Subject): void;
}
// 具体观察者
class ConcreteObserverA implements Observer {public update(subject: Subject): void {if (subject instanceof ConcreteSubject && subject.state < 3) {console.log('ConcreteObserverA: Reacted to the event.');}}
}
class ConcreteObserverB implements Observer {public update(subject: Subject): void {if (subject instanceof ConcreteSubject && (subject.state === 0 || subject.state >= 2)) {console.log('ConcreteObserverB: Reacted to the event.');}}
}
// 客户端代码
const subject = new ConcreteSubject();
const observer1 = new ConcreteObserverA();
subject.attach(observer1); // Subject: Attached an observer.
const observer2 = new ConcreteObserverB();
subject.attach(observer2); // Subject: Attached an observer.
subject.someBusinessLogic(); // Subject: I'm doing something important. Subject: My state has just changed to: 6.  Subject: Notifying observers...  ConcreteObserverB: Reacted to the event.    
subject.someBusinessLogic(); // Subject: I'm doing something important. Subject: My state has just changed to: 1.  Subject: Notifying observers... ConcreteObserverA: Reacted to the event.  
subject.detach(observer2); // Subject: Detached an observer.
subject.someBusinessLogic(); // Subject: I'm doing something important. Subject: My state has just changed to: 5. Subject: Notifying observers...
  • 中介者模式

中介者模式是一种行为设计模式, 能够减少对象之间混乱无序的依赖关系。该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。

举个现实的例子,航班与航班之间不会直接沟通,而是与塔台沟通,从而可来避免航线交叉、重复而可能引起的飞机之间的碰撞。当然现实中还有很多其他的例子,中介者模式其实就是我们字面上理解的中介者的意思。

中介者模式结构:

1096e56d30691193fc929085f9c89de3.png

代码示例:

interface Mediator {notify(sender: object, event: string): void;
}
class ConcreteMediator implements Mediator {private component1: Component1;private component2: Component2;constructor(c1: Component1, c2: Component2) {this.component1 = c1;this.component1.setMediator(this);this.component2 = c2;this.component2.setMediator(this);}public notify(sender: object, event: string): void {if (event === 'A') {console.log('Mediator reacts on A and triggers following operations:');this.component2.doC();}if (event === 'D') {console.log('Mediator reacts on D and triggers following operations:');this.component1.doB();this.component2.doC();}}
}
class BaseComponent {protected mediator: Mediator;constructor(mediator?: Mediator) {this.mediator = mediator!;}public setMediator(mediator: Mediator): void {this.mediator = mediator;}
}
class Component1 extends BaseComponent {public doA(): void {console.log('Component 1 does A.');this.mediator.notify(this, 'A');}public doB(): void {console.log('Component 1 does B.');this.mediator.notify(this, 'B');}
}
class Component2 extends BaseComponent {public doC(): void {console.log('Component 2 does C.');this.mediator.notify(this, 'C');}public doD(): void {console.log('Component 2 does D.');this.mediator.notify(this, 'D');}
}
// 客户端代码
const c1 = new Component1();
const c2 = new Component2();
const mediator = new ConcreteMediator(c1, c2);
// 触发操作A
c1.doA(); // Component 1 does A. Mediator reacts on A and triggers following operations: Component 2 does C.
// 触发操作B 
c2.doD(); // Component 2 does D. Mediator reacts on D and triggers following operations: Component 1 does B. Component 2 does C.

程序组件通过特殊的中介者对象进行间接沟通, 达到了减少组件之间依赖关系的目的。

  • 访问者模式

访问者模式是一种行为设计模式, 它能将算法与其所作用的对象隔离开来。

举个现实的例子,一个房地产销售人员,他可以向各种各样的人推销,如果面对富豪,他推销别墅,如果面对高收入人群,他推销普大平层,如果面对普通收入人群,他推销普通高层。

访问者模式结构:

e4b312dd9307876cfe6cc38e096f190d.png

代码示例:

interface Component {accept(visitor: Visitor): void;
}
class ConcreteComponentA implements Component {public accept(visitor: Visitor): void {visitor.visitConcreteComponentA(this);}public exclusiveMethodOfConcreteComponentA(): string {return 'A';}
}
class ConcreteComponentB implements Component {public accept(visitor: Visitor): void {visitor.visitConcreteComponentB(this);}public specialMethodOfConcreteComponentB(): string {return 'B';}
}
interface Visitor {visitConcreteComponentA(element: ConcreteComponentA): void;visitConcreteComponentB(element: ConcreteComponentB): void;
}
class ConcreteVisitor1 implements Visitor {public visitConcreteComponentA(element: ConcreteComponentA): void {console.log(`${element.exclusiveMethodOfConcreteComponentA()} + ConcreteVisitor1`);}public visitConcreteComponentB(element: ConcreteComponentB): void {console.log(`${element.specialMethodOfConcreteComponentB()} + ConcreteVisitor1`);}
}
class ConcreteVisitor2 implements Visitor {public visitConcreteComponentA(element: ConcreteComponentA): void {console.log(`${element.exclusiveMethodOfConcreteComponentA()} + ConcreteVisitor2`);}public visitConcreteComponentB(element: ConcreteComponentB): void {console.log(`${element.specialMethodOfConcreteComponentB()} + ConcreteVisitor2`);}
}
// 客户端代码
function clientCode(components: Component[], visitor: Visitor) {// ...for (const component of components) {component.accept(visitor);}// ...
}
const components = [new ConcreteComponentA(),new ConcreteComponentB(),
];
// 通过基础访问者接口,客户端代码与所有的访问者一同工作
const visitor1 = new ConcreteVisitor1();
clientCode(components, visitor1); // A + ConcreteVisitor1 B + ConcreteVisitor1
// 同样的客户端代码可以与不同类型的访问者一同工作
const visitor2 = new ConcreteVisitor2();
clientCode(components, visitor2); // A + ConcreteVisitor2 B + ConcreteVisitor2
  • 状态模式

状态模式是一种行为设计模式, 可以在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。

举个实际的例子,你的手机,有话费状态,可以打电话,无话费状态,不可以打电话。设计上就是不同的状态下所对应的功能不同。

状态模式结构:

430d63759429638c00b68bb8e438e3c8.png

代码示例:

class Context {private state: State;constructor(state: State) {this.transitionTo(state);}public transitionTo(state: State): void {console.log(`Context: Transition to ${(<any>state).constructor.name}.`);this.state = state;this.state.setContext(this);}public request1(): void {this.state.handle1();}public request2(): void {this.state.handle2();}
}
abstract class State {protected context: Context;public setContext(context: Context) {this.context = context;}public abstract handle1(): void;public abstract handle2(): void;
}
class ConcreteStateA extends State {public handle1(): void {console.log('ConcreteStateA handles request1.');console.log('ConcreteStateA wants to change the state of the context.');this.context.transitionTo(new ConcreteStateB());}public handle2(): void {console.log('ConcreteStateA handles request2.');}
}
class ConcreteStateB extends State {public handle1(): void {console.log('ConcreteStateB handles request1.');}public handle2(): void {console.log('ConcreteStateB handles request2.');console.log('ConcreteStateB wants to change the state of the context.');this.context.transitionTo(new ConcreteStateA());}
}
// 客户端代码
const context = new Context(new ConcreteStateA());
context.request1(); // Context: Transition to ConcreteStateA. ConcreteStateA handles request1. ConcreteStateA wants to change the state of the context.
context.request2(); // Context: Transition to ConcreteStateB. ConcreteStateB handles request2. ConcreteStateB wants to change the state of the context. Context: Transition to ConcreteStateA.
  • 备忘录模式

备忘录模式是一种行为设计模式, 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态,允许生成对象状态的快照并在以后将其还原,它不会影响它所处理的对象的内部结构, 也不会影响快照中保存的数据。

备忘录模式结构:

9a15bf403a6553d0368edd443b82bdcd.png

代码示例:

class Originator {private state: string;constructor(state: string) {this.state = state;console.log(`Originator: My initial state is: ${state}`);}public doSomething(): void {console.log('Originator: I\'m doing something important.');this.state = this.generateRandomString(30);console.log(`Originator: and my state has changed to: ${this.state}`);}private generateRandomString(length: number = 10): string {const charSet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';return Array.apply(null, { length }).map(() => charSet.charAt(Math.floor(Math.random() * charSet.length))).join('');}public save(): Memento {return new ConcreteMemento(this.state);}public restore(memento: Memento): void {this.state = memento.getState();console.log(`Originator: My state has changed to: ${this.state}`);}
}
interface Memento {getState(): string;getName(): string;getDate(): string;
}
class ConcreteMemento implements Memento {private state: string;private date: string;constructor(state: string) {this.state = state;this.date = new Date().toISOString().slice(0, 19).replace('T', ' ');}public getState(): string {return this.state;}public getName(): string {return `${this.date} / (${this.state.substr(0, 9)}...)`;}public getDate(): string {return this.date;}
}
class Caretaker {private mementos: Memento[] = [];private originator: Originator;constructor(originator: Originator) {this.originator = originator;}public backup(): void {console.log('\nCaretaker: Saving Originator\'s state...');this.mementos.push(this.originator.save());}public undo(): void {if (!this.mementos.length) {return;}const memento = this.mementos.pop();console.log(`Caretaker: Restoring state to: ${memento.getName()}`);this.originator.restore(memento);}public showHistory(): void {console.log('Caretaker: Here\'s the list of mementos:');for (const memento of this.mementos) {console.log(memento.getName());}}
}
// 客户端代码
const originator = new Originator('Super-duper-super-puper-super.');
const caretaker = new Caretaker(originator);caretaker.backup();
originator.doSomething();caretaker.backup();
originator.doSomething();caretaker.backup();
originator.doSomething();console.log('');
caretaker.showHistory();console.log('\nClient: Now, let\'s rollback!\n');
caretaker.undo();console.log('\nClient: Once more!\n');
caretaker.undo();
Originator: My initial state is: Super-duper-super-puper-super.Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: qXqxgTcLSCeLYdcgElOghOFhPGfMxoCaretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: iaVCJVryJwWwbipieensfodeMSWvUYCaretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: oSUxsOCiZEnohBMQEjwnPWJLGnwGmyCaretaker: Here's the list of mementos:
2019-02-17 15:14:05 / (Super-dup...)
2019-02-17 15:14:05 / (qXqxgTcLS...)
2019-02-17 15:14:05 / (iaVCJVryJ...)Client: Now, let's rollback!Caretaker: Restoring state to: 2019-02-17 15:14:05 / (iaVCJVryJ...)
Originator: My state has changed to: iaVCJVryJwWwbipieensfodeMSWvUYClient: Once more!Caretaker: Restoring state to: 2019-02-17 15:14:05 / (qXqxgTcLS...)
Originator: My state has changed to: qXqxgTcLSCeLYdcgElOghOFhPGfMxo
  • 策略模式

策略模式是一种行为设计模式, 能让定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。

举个现实的例子,比如赶火车,到达火车站的策略有公交车、地铁、出租车等,我们可以根据预算或时间等因素选择其中的一种策略。

策略模式结构:

a799668cae5ee511156c2a0828d5b0e2.png

代码示例:

class Context {private strategy: Strategy;constructor(strategy: Strategy) {this.strategy = strategy;}public setStrategy(strategy: Strategy) {this.strategy = strategy;}public doSomeBusinessLogic(): void {console.log('Context: Sorting data using the strategy (not sure how it\'ll do it)');const result = this.strategy.doAlgorithm(['a', 'b', 'c', 'd', 'e']);console.log(result.join(','));}
}
interface Strategy {doAlgorithm(data: string[]): string[];
}
class ConcreteStrategyA implements Strategy {public doAlgorithm(data: string[]): string[] {return data.sort();}
}
class ConcreteStrategyB implements Strategy {public doAlgorithm(data: string[]): string[] {return data.reverse();}
}
// 客户端代码
const context = new Context(new ConcreteStrategyA());
console.log('Client: Strategy is set to normal sorting.');
context.doSomeBusinessLogic();console.log('');console.log('Client: Strategy is set to reverse sorting.');
context.setStrategy(new ConcreteStrategyB());
context.doSomeBusinessLogic();
Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,eClient: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a
  • 模板方法模式

模板方法模式是一种行为设计模式, 它在基类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。

举个现实的例子,农村自建房有一套固定的模版方法(比如为:打地基 -> 建造主体 -> 封顶 -> 铺设水电 -> 装修),但这其中可提供几个扩展点, 允许潜在的房屋业主调整成品房屋的部分细节,如装修风格,这使得成品房屋会有不同。

模版方法模式结构:

0464cf8b049b4caa3909241fc8f7f246.png

代码示例:

abstract class AbstractClass {public templateMethod(): void {this.baseOperation1();this.requiredOperations1();this.baseOperation2();this.hook1();this.requiredOperation2();this.baseOperation3();this.hook2();}protected baseOperation1(): void {console.log('AbstractClass says: I am doing the bulk of the work');}protected baseOperation2(): void {console.log('AbstractClass says: But I let subclasses override some operations');}protected baseOperation3(): void {console.log('AbstractClass says: But I am doing the bulk of the work anyway');}protected abstract requiredOperations1(): void;protected abstract requiredOperation2(): void;protected hook1(): void { }protected hook2(): void { }
}
class ConcreteClass1 extends AbstractClass {protected requiredOperations1(): void {console.log('ConcreteClass1 says: Implemented Operation1');}protected requiredOperation2(): void {console.log('ConcreteClass1 says: Implemented Operation2');}
}
class ConcreteClass2 extends AbstractClass {protected requiredOperations1(): void {console.log('ConcreteClass2 says: Implemented Operation1');}protected requiredOperation2(): void {console.log('ConcreteClass2 says: Implemented Operation2');}protected hook1(): void {console.log('ConcreteClass2 says: Overridden Hook1');}
}
// 客户端代码
function clientCode(abstractClass: AbstractClass) {// ...abstractClass.templateMethod();// ...
}console.log('Same client code can work with different subclasses:');
clientCode(new ConcreteClass1());
console.log('');console.log('Same client code can work with different subclasses:');
clientCode(new ConcreteClass2());
Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass1 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass1 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anywaySame client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass2 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass2 says: Overridden Hook1
ConcreteClass2 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway
  • 职责链模式

职责链模式是一种行为设计模式, 允许将请求沿着处理者链进行发送。收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。

职责链模式结构:

b6b5290a5b3dd78bbd46c49c0400dd03.png

代码示例:

interface Handler {setNext(handler: Handler): Handler;handle(request: string): string;
}
abstract class AbstractHandler implements Handler
{private nextHandler: Handler;public setNext(handler: Handler): Handler {this.nextHandler = handler;return handler;}public handle(request: string): string {if (this.nextHandler) {return this.nextHandler.handle(request);}return null;}
}
class MonkeyHandler extends AbstractHandler {public handle(request: string): string {if (request === 'Banana') {return `Monkey: I'll eat the ${request}.`;}return super.handle(request);}
}
class SquirrelHandler extends AbstractHandler {public handle(request: string): string {if (request === 'Nut') {return `Squirrel: I'll eat the ${request}.`;}return super.handle(request);}
}
class DogHandler extends AbstractHandler {public handle(request: string): string {if (request === 'MeatBall') {return `Dog: I'll eat the ${request}.`;}return super.handle(request);}
}
function clientCode(handler: Handler) {const foods = ['Nut', 'Banana', 'Cup of coffee'];for (const food of foods) {console.log(`Client: Who wants a ${food}?`);const result = handler.handle(food);if (result) {console.log(`  ${result}`);} else {console.log(`  ${food} was left untouched.`);}}
}
// 客户端代码
const monkey = new MonkeyHandler();
const squirrel = new SquirrelHandler();
const dog = new DogHandler();monkey.setNext(squirrel).setNext(dog);console.log('Chain: Monkey > Squirrel > Dog\n');
clientCode(monkey);
console.log('');console.log('Subchain: Squirrel > Dog\n');
clientCode(squirrel);
Chain: Monkey > Squirrel > DogClient: Who wants a Nut?Squirrel: I'll eat the Nut.
Client: Who wants a Banana?Monkey: I'll eat the Banana.
Client: Who wants a Cup of coffee?Cup of coffee was left untouched.Subchain: Squirrel > DogClient: Who wants a Nut?Squirrel: I'll eat the Nut.
Client: Who wants a Banana?Banana was left untouched.
Client: Who wants a Cup of coffee?Cup of coffee was left untouched.
  • 命令模式

命令模式是一种行为设计模式, 它可将请求转换为一个包含与请求相关的所有信息的独立对象。该转换能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作。

命令模式结构:

3bf576886e0b8aba2a0a0ea9bbfea5c7.png

代码示例:

interface Command {execute(): void;
}
class SimpleCommand implements Command {private payload: string;constructor(payload: string) {this.payload = payload;}public execute(): void {console.log(`SimpleCommand: See, I can do simple things like printing (${this.payload})`);}
}
class ComplexCommand implements Command {private receiver: Receiver;private a: string;private b: string;constructor(receiver: Receiver, a: string, b: string) {this.receiver = receiver;this.a = a;this.b = b;}public execute(): void {console.log('ComplexCommand: Complex stuff should be done by a receiver object.');this.receiver.doSomething(this.a);this.receiver.doSomethingElse(this.b);}
}
class Receiver {public doSomething(a: string): void {console.log(`Receiver: Working on (${a}.)`);}public doSomethingElse(b: string): void {console.log(`Receiver: Also working on (${b}.)`);}
}
class Invoker {private onStart: Command;private onFinish: Command;public setOnStart(command: Command): void {this.onStart = command;}public setOnFinish(command: Command): void {this.onFinish = command;}public doSomethingImportant(): void {console.log('Invoker: Does anybody want something done before I begin?');if (this.isCommand(this.onStart)) {this.onStart.execute();}console.log('Invoker: ...doing something really important...');console.log('Invoker: Does anybody want something done after I finish?');if (this.isCommand(this.onFinish)) {this.onFinish.execute();}}private isCommand(object): object is Command {return object.execute !== undefined;}
}
// 客户端代码
const invoker = new Invoker();
invoker.setOnStart(new SimpleCommand('Say Hi!'));
const receiver = new Receiver();
invoker.setOnFinish(new ComplexCommand(receiver, 'Send email', 'Save report'));invoker.doSomethingImportant();
Invoker: Does anybody want something done before I begin?
SimpleCommand: See, I can do simple things like printing (Say Hi!)
Invoker: ...doing something really important...
Invoker: Does anybody want something done after I finish?
ComplexCommand: Complex stuff should be done by a receiver object.
Receiver: Working on (Send email.)
Receiver: Also working on (Save report.)

5e500b180ff8326978e330c5c1574221.png

参考文献

Alexander Shvets, Dive-into Design Patterns[M], Finelybook, 2019.

70ceb186725598c03aa268fe0518c5d4.png

团队介绍

我们是大淘宝技术-营销与平台策略技术-营销产品团队,主要负责淘宝好价、百亿补贴、聚划算、天天特卖等大淘宝核心产品及各类大型的营销会场活动,同时我们也是前端智能化的先锋队,有方舟、千帆、imgcook、智能UI等多种技术产品。

¤ 拓展阅读 ¤

3DXR技术 | 终端技术 | 音视频技术

服务端技术 | 技术质量 | 数据算法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/4051.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java解决new date出现的时区问题(差8小时)

1、设置当前时区 SimpleDateFormat format new SimpleDateFormat("yyyy/MM/dd"); format.setTimeZone(TimeZone.getTimeZone("GMT8:00")); 2、设置全局时区 创建一个全局配置类&#xff0c;用于配置项目全局时区。 这样就不用专门在各个地方设置时区了…

干货!3个技巧让你轻松增强客户实时聊天的体验感

在当今竞争激烈的商业环境中&#xff0c;提供出色的客户服务成为企业成功的关键要素之一。尤其是在实时聊天平台上&#xff0c;为客户提供优质的体验感&#xff0c;对于建立良好的客户关系和提高销售转化率至关重要。如果你还在苦恼如何增强用户体验感&#xff0c;苦恼如何增加…

剑指offer刷题笔记--Num51-60

1--数组中的逆序对&#xff08;51&#xff09; 主要思路&#xff1a; 基于归并排序&#xff0c;视频讲解参考&#xff1a;数组中的逆序对 #include <iostream> #include <vector>class Solution { public:int reversePairs(std::vector<int>& nums) {if(…

iOS-Block

Blocks的学习 Block的分类 Block根据其类型可以分为三类&#xff1a; 全局Block&#xff08;NSGlobalBlock&#xff09;栈Block&#xff08;NSMallocBlock&#xff09;堆Block&#xff08;NSStackBlock&#xff09; 而其区分的规则为&#xff1a; 如果没有引用局部变量&…

arping命令 ip地址冲突检测 根据ip查mac地址

arping命令介绍 arping 命令主要用来获取ip对应的mac地址&#xff0c;更新本地arp缓存表。平时主要用来探测ip地址是否冲突即同一个网络里&#xff0c;同一个ip不同mac地址的情况。ip地址冲突将导致网络故障。 arping常用命令参数 arping [参数] ip -U 强制更新邻近主机的a…

关于电脑显示器屏幕看不出灰色,灰色和白色几乎一样无法区分,色彩调整方法

问题&#xff1a; 电脑显示器屏幕看不出灰色&#xff0c;灰色和白色几乎一样无法区分。白色和灰色有色差。 解决方法&#xff1a; 打开“控制面板” ->“色彩管理” ->“高级” ->“校正显示器” 在下一步调节中调成中间这一个实例的样子就可以了 进行微调&#x…

【hadoop】部署hadoop全分布模式

hadoop全分布模式 全分布模式特点部署全分布模式准备工作正式配置hadoop-env.shhdfs-site.xmlcore-site.xmlmapred-site.xmlyarn-site.xmlslaves对NameNode进行格式化复制到另外两台虚拟机启动 对部署是否成功进行测试 全分布模式特点 真正的分布式环境&#xff0c;用于生产具…

【Vue】day02-Vue基础入门

目录 day02 一、今日学习目标 1.指令补充 2.computed计算属性 3.watch侦听器 4.综合案例 &#xff08;演示&#xff09; 二、指令修饰符 1.什么是指令修饰符&#xff1f; 2.按键修饰符 3.v-model修饰符 4.事件修饰符 三、v-bind对样式控制的增强-操作class 1.语法…

边缘检测之loG算子

note // 边缘检测之loG算子&#xff1a;对高斯函数求二阶导数 // G(x,y) exp(-1 * (x*x y*y) / 2 / sigma / sigma) // loG(x,y) ((x*x y*y - 2 * sigma * sigma) / (sigma^4)) * exp(-1 * (x*x y*y) / 2 / sigma /sigma) /* [ 0,0,-1,0,0; 0,-1,-2,-1,0; -1,-2,16,-2…

uni-app实现emoj表情包发送(nvue版)

uni-app实现表情包发送&#xff0c; vue实现思路直接使用grideview网格布局加载emoj表情包即可实现&#xff0c;很简单&#xff0c;但是nvue稍微复杂&#xff0c;这里采用的方案是nvue提供的组件list 看效果 代码 <template><view style"margin-right: 10rpx;m…

如何使用自有数据微调ChatGLM-6B

构建自己的数据集 数据格式&#xff1a;问答对 官网例子 ADGEN 数据集任务为根据输入&#xff08;content&#xff09;生成一段广告词&#xff08;summary&#xff09;。 { "content": "类型#上衣*版型#宽松*版型#显瘦*图案#线条*衣样式#衬衫*衣袖型#泡泡袖…

3.8 Bootstrap 面包屑导航(Breadcrumbs)

文章目录 Bootstrap 面包屑导航&#xff08;Breadcrumbs&#xff09; Bootstrap 面包屑导航&#xff08;Breadcrumbs&#xff09; 面包屑导航&#xff08;Breadcrumbs&#xff09;是一种基于网站层次信息的显示方式。以博客为例&#xff0c;面包屑导航可以显示发布日期、类别或…

Stable Diffusion + EbSynth + ControlNet 解决生成视频闪烁

一、安装 1.1、安装ffmpeg 下载地址&#xff1a; 解压&#xff0c;配置环境变量 E:\AI\ffmpeg\bin 检查是否安装成功 1.2、安装SD的 EbSynth 插件 插件地址 https://github.com/s9roll7/ebsynth_utility 报错&#xff1a;ModuleNotFoundError: No module named extension…

【广州华锐互动】AR远程巡检系统在设备维修保养中的作用

随着科技的不断发展&#xff0c;AR(增强现实)远程巡检系统在设备检修中发挥着越来越重要的作用。这种系统可以将AR技术与远程通信技术相结合&#xff0c;实现对设备检修过程的实时监控和远程指导&#xff0c;提高设备检修的效率和质量。 首先&#xff0c;AR远程巡检系统可以帮助…

单片机尽力少用位域操作

1、在51单片机中少用uint32_t类型&#xff0c;查看汇编真的好多条指令&#xff0c;尽力避免少用。 2、在32位单片机中&#xff0c;u8、u16、u32类型操作起来基本没有什么影响&#xff0c;下图是我做的测试&#xff0c;可能测试不全面&#xff0c;按照当前测试&#xff0c;在32…

Kubernetes_1.27.3_Harbor结合Nacos实战

Nacos 实战 作者:行癫(盗版必究) 一:Nacos简介 1.简介 ​ Nacos是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台;Nacos 致力于帮助您发现、配置和管理微服务;Nacos 提供了一组简单易用的特…

实战 ➾【Red Hat 搭建部署VSFTPd服务】

实战 ➾【Red Hat 搭建部署VSFTPd服务】 &#x1f53b; 前言&#x1f53b; 一、vsFTPd服务部署&#x1f6a5; 1.1 vsFTPd服务安装&#x1f6a5; 1.2 vsFTPd服务的启动与关闭 &#x1f53b; 二、vsFTPd相关配置&#x1f6a5; 2.1 vsFTPd的相关配置文件&#x1f6a5; 2.2 配置匿名…

SpringSecurity(6.1.x版本) 认证,授权,自定义登录,内部机制探讨

SpringSecurity 文章目录 SpringSecurityCSRF跨站请求伪造攻击SFA会话固定攻击XSS跨站脚本攻击开发环境搭建认证直接认证使用数据库认证自定义验证 其他配置自定义登录界面记住我功能 授权基于角色授权基于权限授权使用注解权限判断 内部机制探究授权校验流程安全上下文安全上下…

【JAVA】方法的使用:方法语法、方法调用、方法重载、递归练习

&#x1f349;内容专栏&#xff1a;【JAVA从0到入门】 &#x1f349;本文脉络&#xff1a;JAVA方法的使用&#xff0c;递归练习 &#x1f349;本文作者&#xff1a;Melon_西西 &#x1f349;发布时间 &#xff1a;2023.7.19 目录 1. 什么是方法(method) 2 方法定义 2.1 方法…

C语言——文件操作(超全超详细)

C语言——文件操作 1. 什么是文件 磁盘上的文件是文件 但是在程序设计中&#xff0c;我们一般谈的文件有两种&#xff1a;程序文件、数据文件&#xff08;从文件功能的角度来分类的&#xff09; 1.1 程序文件 包括源程序文件&#xff08;后缀为.c&#xff09;&#xff0c;目…