Application of design patterns in front-end engineering

8bee58143225525314ba9af0a5e0ae18.gif

This article aims to systematically introduce 23 design patterns, and give easy-to-understand cases, structure diagrams and code examples. This is also the process of my own learning and understanding. Perhaps several of the design patterns are not very clear and easy to understand, and more detailed ones can be studied in depth according to the references mentioned.

37b0bf9d277c01ef0b7955755d129514.png

what is design pattern

The concept of design patterns was popularized by a book called "Design Patterns: Fundamentals of Reusable Object-Oriented Software", written in 1994 by four C++ engineers. This book explores the power and pitfalls of object-oriented programming and introduces 23 patterns you can use to solve programming problems. These patterns are not algorithms or specific implementations, they are more like ideas, viewpoints and abstractions, helping us to solve some specific problems. But these patterns are built on the basis of C++'s OOP. When using more modern programming languages ​​​​such as JavaScript, the patterns may not be equivalent, and even add unnecessary restrictions to the code, but we can still use these patterns as some programming. knowledge to learn and understand. The purpose of our learning to use these patterns is to achieve high cohesion and low coupling of the code, improve the reusability of the code, make the code easier for others to understand and ensure the reliability of the code.

9a3e02b804a085218ac3284b7ebd786d.png

Introduction to 23 Design Patterns

▐Creational   (5 types)

Provides a mechanism for creating objects, increasing the flexibility and reusability of existing code.

  • Singleton

Ensure that a class has only one instance, and provide a global node to access that instance.

  • Prototype

Allows an object to be used as a blueprint to create another object, and the new object inherits the properties and methods of the original object.

  • Builder pattern (Builder)

Objects are created in "steps".

  • Factory Method Pattern (Factory Method)

Provides an interface for creating objects, which can be modified after they are created.

  • Abstract Factory Pattern (Abstract Factory)

Allows generating a series of related objects without specifying a concrete class.

▐Structural   (7 types)

Describes how to assemble objects and classes into larger structures while keeping the structure flexible and efficient.

  • Bridge mode (Bridge)

Separate the abstraction from its implementation so that they can both vary independently.

  • Appearance mode (Facade)

Provides simplified interfaces to libraries, frameworks, and other complex collections.

  • Composite mode (Composite)

Group objects into tree structures to represent whole and part hierarchies

  • Decorator pattern (Decorator)

Add a new behavior to the original object by adding a modified object to wrap the original object

  • Adapter pattern (Adapter)

Allows two objects with incompatible interfaces to interact with each other

  • Proxy mode (Proxy)

Provides a replacement or placeholder for another object

  • Flyweight

Use sharing technology to effectively support the reuse of a large number of fine-grained objects to reduce the number of objects created

   Behavioral (11 types)

Responsible for efficient communication and delegation of responsibilities between objects.

  • Iterator pattern (Iterator)

The elements used to iterate over the collection

  • Interpreter mode (Interpreter)

Given a language, define a representation of its grammar, and define an interpreter that uses this representation to interpret sentences in the language

  • Observer pattern (Observer)

Define a subscription mechanism to notify multiple objects of any events that occur on the objects they are observing

  • Mediator mode (Mediator)

Use an intermediary object to encapsulate complex interactions between multiple objects

  • Visitor pattern (Visitor)

For elements in an object's structure, define new methods for accessing elements in its structure without changing the object.

  • State mode (State)

When an object's internal state changes, resulting in a change in its behavior, this appears to change the object

  • Memento mode (Memento)

Allows saving and restoring the previous state of an object without exposing the details of the object's implementation.

  • Strategy

Define a series of algorithms, encapsulate them one by one, and make them interchangeable.

  • Template Method pattern (Template Method)

A set of operation algorithm skeletons are defined in the parent class, and some implementation steps are delayed to the subclass, so that the subclass can redefine some implementation steps in the algorithm without changing the algorithm structure of the parent class.

  • Chain of Responsibility

The request is passed through a chain of handlers, where each handler in the chain decides to either process the request or pass it on to the next handler in the chain.

  • Command mode (Command)

Wrap the request in an object in the form of a command and pass it to the calling object. The calling object looks for a suitable object that can handle the command, and passes the command to the corresponding object, which executes the command.

704a6b253ee2197c5726ade860ab60d8.png

Detailed design pattern

▐Creative   type

Creational patterns provide a mechanism for creating objects, which can improve the flexibility and reusability of existing code.

  • singleton pattern

The singleton pattern is a creational design pattern that ensures that a class has only one instance and provides a global node to access that instance.

To give a realistic example, say a country has only one official government. Regardless of the identity of everyone who makes up the government, the title "some government" always identifies the global access node for those in power.

Singleton pattern structure:

6c639eea863988c9f1dc9f78a0ed6301.png

Code:

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(); // 同一个实例
  • prototype pattern

The prototype pattern is a creational design pattern that can copy existing objects and obtain the properties and methods of the original objects without making the code depend on the class they belong to.

To give a realistic example, such as cell mitosis. Mitosis produces a pair of identical cells. The protocell is a prototype that drives the production of replicas.

Prototype schema structure:

9eb2908942ddf5b86d49a56b9e1b98e1.png

Code:

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(); // 属性复制成功   方法复制成功,并不依赖于原先的类
  • generator pattern

The constructor pattern is a creational design pattern that can create complex objects in steps, and can use the same creation code to generate objects of different types and forms.

To give a realistic example, such as car manufacturing, factory A produces tires, factory B produces doors, factory C produces glass, ... and finally factory Z is responsible for assembling these parts to produce a complete car.

Builder pattern structure:

7be2a3da082b4f0f3e2668dd0709b3ee.png

In short, the builder pattern can be summarized into two parts. The first part is that the specific builder (ConcreteBuilder) is responsible for new Product and provides various methods for processing Product; the second part is that the director (Director) is responsible for processing various methods for Product. call.

Code:

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: PartA1


    console.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);
  • factory method pattern

The factory method pattern is a creational design pattern that provides a method of creating an object in the parent class and allows the subclass to determine the type of instantiated object.

Factory method pattern structure:

138aaa32f1d8c2b67441dc6ff6fc64c3.png

Code:

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的结果}

In short, the client passes in a specific generator, and then creates a new product, and different specific generators can produce different specific products. That is, the parent class provides a method to create an object, allowing the subclass to determine the type of instantiated object.

  • abstract factory pattern

The abstract factory pattern is a creational design pattern that can create a series of related objects without specifying their concrete classes.

Abstract factory pattern structure:

7e8064ea63bfb8f5dae2441ba7fe23da.png

Code:

// 抽象工厂接口,声明可返回不同抽象产品的方法
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)

In short, the input of the client is a concrete factory (the concrete factory is the implementation of the abstract factory), and the concrete factory will generate specific products. The abstract factory provides you with an interface that can be used to create objects for each series of products. As long as your code creates objects through this interface, then you won't generate products that are inconsistent with the types of products your application has already generated.

▐Structural   type

Structural patterns describe how to assemble objects and classes into larger structures while keeping the structure flexible and efficient.

  • bridge mode

The bridge pattern is a structural design pattern that can split a large class or a series of closely related classes into different hierarchies so that they can be used separately during development.

Bridge mode structure:

ed16dbb3fabab2c932968ac3f33694c6.png

Code:

// 定义了所有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的结果.

To put it simply, for a complex and huge system, it can be layered. The first layer is the abstract layer, and the second layer is the concrete layer. The abstract layer can delegate its calls to the implementation layer, and all the implementation layers All have a unified interface, so the implementation layer can replace each other within the abstract part.

  • appearance mode

A facade pattern is a structural design pattern that provides a simple interface to a library, framework, or other complex class.

To give a realistic example, Taobao is an appearance, which provides interfaces for you to shop, pay, and send flowers to your door.

Façade schema structure:

99ee04a195ad20e1bc8ac3581e9d5e77.png

Code:

// 为一个或多个子系统的复杂逻辑提供简单接口
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执行!
  • combination mode

The Composite pattern is a structural design pattern that allows you to compose objects into a tree structure and use them as if they were individual objects.

To give a realistic example, such as a school, the school is composed of grades, the grades are composed of classes, and the classes are composed of individual students. When the school issues important notices, it is issued level by level, passing through levels one by one, until every student receives the notice.

Composite schema structure:

9a5c715c224df1d5b1ef90fad7e829e4.png

Code example:

// 描述了 简单 和 复杂 所共有的操作
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: Leaf


const 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)

Simply put, the main function of the composite pattern is to recursively call methods on the entire tree structure and summarize the results.

  • decorator pattern

The decorator pattern is a structural design pattern that allows you to bind new behavior to the original object by placing the object in a special wrapper object that contains the behavior.

To give a realistic example, for example, add clothes when it is cold. If you feel cold, you can add a sweater, and if you feel cold, you can add a down jacket. These clothes extend your basic behavior, but they are not part of you and can be removed at any time if they are no longer needed. If it rains, you can always add a raincoat.

Decorator pattern structure:

26733854fdb9ab50e22e8e1e579f1717.png

Code example:

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: ConcreteComponent


const decorator1 = new ConcreteDecoratorA(simple);
const decorator2 = new ConcreteDecoratorB(decorator1);
console.log('Client: 装饰器部件:');
clientCode(decorator2); // RESULT: ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))

In short, the target object and the decorator follow the same interface, so the object can be encapsulated infinitely with decoration, and the resulting object will get the superposition result of all the decorators.

  • adapter pattern

The adapter pattern is a structural design pattern that enables objects with incompatible interfaces to cooperate with each other.

To give a realistic example, the power adapter.

Adapter pattern structure:

4acd4ee471a6c056e8eaeea9d594fb9f.png

Code example:

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 laicepS


const adapter = new Adapter(adaptee);
clientCode(adapter); // Adapter: (TRANSLATED) Special behavior of the Adaptee.

In short, an adapter takes a call to one object and translates it into a format and interface recognized by another object.


  • Proxy mode

The proxy pattern is a structural design pattern that provides a substitute or placeholder for an object. The proxy controls access to the original object and allows some processing before and after the request is submitted to the object.

To give a realistic example, our digital payment tools such as Alipay and WeChat Pay are agents of cash. They all implement the same interface and can be used for payment. Consumers and consumers will be very satisfied, because both parties are very convenient.

Proxy schema structure:

4e49418d0de9f9aab6eb8f631e92a214.png

Code example:

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); // 代理:触发真正的请求之前进行检查访问. 实际服务者: 处理请求. 代理:请求之后的一些处理.
  • Flyweight mode

Flyweight mode is a structural design mode, which abandons the way of saving all data in each object, and allows you to load more objects in a limited memory capacity by sharing the same state shared by multiple objects .

Enjoy the meta-schema structure:

3acd87fbb44072a6e8f79fd682b1d6f0.png

Code example:

// 存储共有状态,接收其余状态
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

To put it simply, the flyweight mode realizes the above functions by sharing partial states of multiple objects, and the flyweight caches the same data of different objects to save memory. As in the above example, the model of the car will be stored in the Flyweight, which is exclusive to the owner of the car. When adding an owner-vehicle data into the database, Flyweight Factory first checks whether the data of this model of car already exists in Flyweight, and returns it directly if there is, and creates this model if not.

Behavior type  

Behavioral patterns are responsible for efficient communication and delegation of responsibilities between objects.

  • iterator pattern

The iterator pattern is a behavioral design pattern that can traverse all elements in a collection without exposing the underlying representation of the collection (list, stack, tree, etc.).

To give a realistic example, if you travel to Beijing, the Forbidden City, the Great Wall, Tiananmen Square, the Summer Palace, Peking University, and Tsinghua University are the destinations of this trip, and you can form them into a collection of destinations. The first way to visit is to play in the order of your own wishes, the second way is to follow the order of tours recommended by some bloggers, and the third way is to sign up for a tour group and play in the order arranged by the tour group. The above three choices are the iterators of the destination collection.

Iterator pattern structure:

8e246b51009d3ea7f97c601bf05d3d2d.png

Code example:

interface Iterator<T> {
    current(): T; // 返回当前的元素
    next(): T; // 返回下一个元素
    key(): number; // 返回当前元素的key
    valid(): 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
}

The easiest way to understand it is actually what we often call iterators in ES6.

  • interpreter mode

The interpreter pattern is a behavioral design pattern. Given a language, define a representation of its grammar, and define an interpreter that uses this representation to interpret sentences in the language.

Code example:

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
  • Observer pattern

The observer pattern is a behavioral design pattern, also known as the publish-subscribe pattern. It allows defining a subscription mechanism that notifies multiple other objects "observing" an object when an object event occurs.

To give a realistic example, for example, if you subscribe to the People's Daily, whenever it is released, a postman will deliver the newspaper to you, without you having to go to the newsstand to buy it yourself. The publishing house is the publisher, and you are the subscriber. The publishing house maintains a list of subscribers. When you no longer want to subscribe, you can apply to withdraw from the subscription.

Observer pattern structure:

c04b441d2b33f672bacd26945383f5b5.png

Code example:

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...
  • mediator pattern

The Mediator pattern is a behavioral design pattern that reduces messy dependencies between objects. This pattern restricts direct interaction between objects, forcing them to cooperate through a mediator object.

To give a realistic example, flights do not communicate directly with each other, but communicate with the tower, so as to avoid collisions between aircrafts that may be caused by crossed and repeated routes. Of course, there are many other examples in reality. The intermediary model is actually the meaning of intermediary we understand literally.

Mediator pattern structure:

1096e56d30691193fc929085f9c89de3.png

Code example:

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.

Program components communicate indirectly through special intermediary objects, achieving the purpose of reducing dependencies between components.

  • visitor pattern

The Visitor pattern is a behavioral design pattern that isolates an algorithm from the objects it operates on.

To give a realistic example, a real estate salesperson can sell to all kinds of people. If he is facing rich people, he will sell villas; , he sells ordinary high-level.

Visitor pattern structure:

e4b312dd9307876cfe6cc38e096f190d.png

Code example:

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
  • state mode

The state pattern is a behavior design pattern that can change the behavior of an object when its internal state changes, making it look like it has changed its own class.

To give a practical example, your mobile phone can make calls if it has a call charge status, but cannot make calls if it has no call charge status. The design is that different states correspond to different functions.

State schema structure:

430d63759429638c00b68bb8e438e3c8.png

Code example:

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.
  • memo mode

The memento pattern is a behavioral design pattern that allows saving and restoring the previous state of an object without exposing the details of the object's implementation. The internal structure will not affect the data saved in the snapshot.

Memo schema structure:

9a15bf403a6553d0368edd443b82bdcd.png

Code example:

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: qXqxgTcLSCeLYdcgElOghOFhPGfMxo


Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: iaVCJVryJwWwbipieensfodeMSWvUY


Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: oSUxsOCiZEnohBMQEjwnPWJLGnwGmy


Caretaker: 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: iaVCJVryJwWwbipieensfodeMSWvUY


Client: Once more!


Caretaker: Restoring state to: 2019-02-17 15:14:05 / (qXqxgTcLS...)
Originator: My state has changed to: qXqxgTcLSCeLYdcgElOghOFhPGfMxo
  • strategy pattern

The strategy pattern is a behavioral design pattern that enables the definition of a series of algorithms and puts each algorithm into an independent class so that the objects of the algorithm can be replaced with each other.

To give a realistic example, such as catching a train, the strategies for arriving at the train station include bus, subway, taxi, etc. We can choose one of these strategies based on factors such as budget or time.

Strategy pattern structure:

a799668cae5ee511156c2a0828d5b0e2.png

Code example:

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,e


Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a
  • template method pattern

The template method pattern is a behavioral design pattern that defines the framework of an algorithm in a base class, allowing subclasses to rewrite specific steps of the algorithm without modifying the structure.

To give a realistic example, rural self-built houses have a set of fixed template methods (such as: laying foundation -> building the main body -> capping -> laying water and electricity -> decoration), but it can provide several extension points, allowing potential Most house owners adjust some details of the finished house, such as the decoration style, which makes the finished house different.

Template method pattern structure:

0464cf8b049b4caa3909241fc8f7f246.png

Code example:

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 anyway


Same 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
  • Chain of Responsibility Pattern

The Chain of Responsibility pattern is a behavioral design pattern that allows requests to be sent along a chain of handlers. After receiving the request, each processor can process the request or pass it to the next processor in the chain.

Responsibility chain pattern structure:

b6b5290a5b3dd78bbd46c49c0400dd03.png

Code example:

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 > Dog


Client: 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 > Dog


Client: 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.
  • command mode

The command pattern is a behavioral design pattern that transforms a request into a single object that contains all information related to the request. The transformation can parameterize the method, delay the execution of the request or put it in the queue according to different requests, and can implement undoable operations.

Command mode structure:

3bf576886e0b8aba2a0a0ea9bbfea5c7.png

Code example:

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

references

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

70ceb186725598c03aa268fe0518c5d4.png

team introduction

We are Big Taobao Technology-Marketing and Platform Strategy Technology-Marketing Product Team, mainly responsible for Taobao’s core products such as Taobao Good Price, Ten Billion Subsidy, Juhuasuan, Tiantian Special Sale and various large-scale marketing venue activities. At the same time, we are also the front-end intelligence The vanguard of globalization has a variety of technical products such as Ark, Qianfan, imgcook, and smart UI.

¤  Extended reading  ¤

3DXR Technology  |  Terminal Technology  |  Audio and Video Technology

Server Technology  |  Technical Quality  |  Data Algorithms

Guess you like

Origin blog.csdn.net/Taobaojishu/article/details/131777909