フロントエンドエンジニアリングにおけるデザインパターンの適用

8bee58143225525314ba9af0a5e0ae18.gif

この記事では、23のデザインパターンを体系的に紹介し、分かりやすい事例、構造図、コード例を示すことを目的としており、これは私自身の学習と理解のプロセスでもあります。おそらく、いくつかの設計パターンはあまり明確ではなく、理解しやすいものではありません。より詳細な設計パターンについては、前述の参考文献に従って詳しく調べることができます。

37b0bf9d277c01ef0b7955755d129514.png

デザインパターンとは何ですか

デザイン パターンの概念は、1994 年に 4 人の C++ エンジニアによって書かれた『デザイン パターン: 再利用可能なオブジェクト指向ソフトウェアの基礎』という書籍によって普及しました。この本では、オブジェクト指向プログラミングの威力と落とし穴を探り、プログラミングの問題を解決するために使用できる 23 のパターンを紹介します。これらのパターンはアルゴリズムや特定の実装ではなく、アイデア、視点、抽象化に似ており、特定の問題を解決するのに役立ちます。ただし、これらのパターンは C++ の OOP に基づいて構築されています。JavaScript などの最新のプログラミング言語を使用する場合、パターンは同等ではない可能性があり、コードに不必要な制限が追加される場合もありますが、これらのパターンは依然として次のように使用できます。いくつかのプログラミング、学び理解するための知識。これらのパターンの使用方法を学習する目的は、コードの高い凝集性と低い結合性を実現し、コードの再利用性を向上させ、他の人にとってコードを理解しやすくし、コードの信頼性を確保することです。

9a3e02b804a085218ac3284b7ebd786d.png

23のデザインパターンの紹介

▐クリエイティブ  (5種類)

オブジェクトを作成するメカニズムを提供し、既存のコードの柔軟性と再利用性を高めます。

  • シングルトン

クラスにインスタンスが 1 つだけあることを確認し、そのインスタンスにアクセスするためのグローバル ノードを提供します。

  • プロトタイプ

オブジェクトを別のオブジェクトを作成するためのブループリントとして使用できるようにし、新しいオブジェクトは元のオブジェクトのプロパティとメソッドを継承します。

  • ビルダーパターン(ビルダー)

オブジェクトは「ステップ」で作成されます。

  • ファクトリーメソッドパターン (ファクトリーメソッド)

オブジェクトを作成するためのインターフェイスを提供します。オブジェクトは作成後に変更できます。

  • 抽象ファクトリー パターン (抽象ファクトリー)

具体的なクラスを指定せずに、一連の関連オブジェクトを生成できるようにします。

▐構造  (7種類)

構造を柔軟かつ効率的に保ちながら、オブジェクトとクラスをより大きな構造に組み立てる方法について説明します。

  • ブリッジモード(ブリッジ)

抽象化をその実装から分離して、両方が独立して変更できるようにします。

  • 外観モード(ファサード)

ライブラリ、フレームワーク、その他の複雑なコレクションに簡素化されたインターフェイスを提供します。

  • コンポジットモード(コンポジット)

オブジェクトをツリー構造にグループ化して、全体および部分の階層を表現します。

  • デコレータパターン(デコレータ)

変更されたオブジェクトを追加して元のオブジェクトをラップすることにより、元のオブジェクトに新しい動作を追加します。

  • アダプターパターン(アダプター)

互換性のないインターフェイスを持つ 2 つのオブジェクトが相互に対話できるようにします

  • プロキシモード(プロキシ)

別のオブジェクトの代替またはプレースホルダーを提供します

  • フライ級

共有テクノロジーを使用して、多数のきめ細かいオブジェクトの再利用を効果的にサポートし、作成されるオブジェクトの数を削減します。

  行動(11種類)

オブジェクト間の効率的なコミュニケーションと責任の委任を担当します。

  • イテレータパターン(イテレータ)

コレクションの反復処理に使用される要素

  • 通訳モード(通訳)

言語を指定して、その文法の表現を定義し、この表現を使用してその言語の文を解釈するインタプリタを定義します。

  • オブザーバーパターン(オブザーバー)

監視しているオブジェクトで発生するイベントを複数のオブジェクトに通知するためのサブスクリプション メカニズムを定義します。

  • メディエーターモード(メディエーター)

中間オブジェクトを使用して、複数のオブジェクト間の複雑な相互作用をカプセル化します。

  • 訪問者パターン(訪問者)

オブジェクトの構造内の要素については、オブジェクトを変更せずにその構造内の要素にアクセスするための新しいメソッドを定義します。

  • ステートモード(State)

オブジェクトの内部状態が変化し、その動作が変化すると、オブジェクトが変化したように見えます。

  • メメントモード(メメント)

オブジェクトの実装の詳細を公開せずに、オブジェクトの以前の状態を保存および復元できるようにします。

  • ストラテジー

一連のアルゴリズムを定義し、それらを 1 つずつカプセル化し、交換可能にします。

  • テンプレートメソッドパターン(テンプレートメソッド)

一連の操作アルゴリズム スケルトンは親クラスで定義され、一部の実装ステップはサブクラスに遅延されるため、サブクラスは親クラスのアルゴリズム構造を変更せずにアルゴリズムの一部の実装ステップを再定義できます。

  • 責任の連鎖

リクエストはハンドラーのチェーンを介して渡され、チェーン内の各ハンドラーはリクエストを処理するか、チェーン内の次のハンドラーにリクエストを渡すかを決定します。

  • コマンドモード(コマンド)

リクエストをコマンド形式のオブジェクトにラップし、呼び出し元のオブジェクトに渡します。呼び出し元のオブジェクトは、コマンドを処理できる適切なオブジェクトを探し、対応するオブジェクトにコマンドを渡し、そこでコマンドが実行されます。

704a6b253ee2197c5726ade860ab60d8.png

詳細なデザインパターン

▐クリエイティブ  タイプ

作成パターンはオブジェクトを作成するためのメカニズムを提供し、既存のコードの柔軟性と再利用性を向上させることができます。

  • シングルトンパターン

シングルトン パターンは、クラスにインスタンスが 1 つだけあることを保証し、そのインスタンスにアクセスするためのグローバル ノードを提供する作成設計パターンです。

現実的な例を挙げると、ある国に正式な政府が 1 つだけあるとします。政府を構成する全員の身元に関係なく、「ある政府」というタイトルは常に権力者にとってのグローバルなアクセスノードを特定します。

シングルトンパターン構造:

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) が新しいプロダクトを担当し、プロダクトを処理するためのさまざまなメソッドを提供するという 2 つの部分に要約できます。 Product.call のさまざまなメソッドを処理します。

コード:

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);
  • ファクトリメソッドパターン

ファクトリ メソッド パターンは、親クラスでオブジェクトを作成するメソッドを提供し、サブクラスがインスタンス化されたオブジェクトのタイプを決定できるようにする作成設計パターンです。

ファクトリ メソッド パターンの構造:

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

簡単に言うと、複雑で巨大なシステムの場合、階層化することができます。最初の層は抽象層、2 番目の層は具象層です。抽象層はその呼び出しを実装層に委任し、すべての実装を委任できます。すべての層は統一されたインターフェイスを備えているため、実装層は抽象部分内で相互に置き換えることができます。

  • 外観モード

ファサード パターンは、ライブラリ、フレームワーク、またはその他の複雑なクラスに単純なインターフェイスを提供する構造設計パターンです。

現実的な例を挙げると、タオバオは、買い物をし、支払いをし、自宅に花を届けるためのインターフェイスを提供する外観です。

ファサードスキーマ構造:

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执行!
  • コンビネーションモード

複合パターンは、オブジェクトをツリー構造に構成し、それらを個別のオブジェクトであるかのように使用できるようにする構造設計パターンです。

学校のような現実的な例を挙げると、学校は学年で構成され、学年はクラスで構成され、クラスは個々の生徒で構成されます。学校が重要な通知を発行する場合、すべての生徒が通知を受け取るまで、レベルごとに発行され、レベルを 1 つずつ通過します。

複合スキーマ構造:

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

簡単に言えば、複合パターンの主な機能は、ツリー構造全体でメソッドを再帰的に呼び出し、結果を要約することです。

  • デコレータパターン

デコレータ パターンは、動作を含む特別なラッパー オブジェクトにオブジェクトを配置することによって、新しい動作を元のオブジェクトにバインドできるようにする構造設計パターンです。

現実的な例で言うと、例えば寒ければ服を追加する、寒いと感じたらセーターを追加する、寒いと感じたらダウンジャケットを追加する、といった具合です。これらの服はあなたの基本的な行動を拡張しますが、それらはあなたの一部ではないため、必要がなくなったらいつでも脱ぐことができます。雨が降った場合は、いつでもレインコートを追加できます。

デコレータ パターンの構造:

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


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


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

つまり、アダプターは 1 つのオブジェクトへの呼び出しを受け取り、それを別のオブジェクトによって認識される形式とインターフェイスに変換します。


  • プロキシモード

プロキシ パターンは、オブジェクトの代替またはプレースホルダーを提供する構造設計パターンです。プロキシは、元のオブジェクトへのアクセスを制御し、リクエストがオブジェクトに送信される前後に一部の処理を許可します。

現実的な例を挙げると、Alipay や WeChat Pay などのデジタル決済ツールは現金の代理店であり、すべて同じインターフェイスを実装しており、支払いに使用できます。消費者と消費者は双方とも非常に便利なので、非常に満足するでしょう。

プロキシスキーマ構造:

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

簡単に言うと、フライウェイト モードは複数のオブジェクトの部分的な状態を共有することで上記の機能を実現し、フライウェイトはメモリを節約するために異なるオブジェクトの同じデータをキャッシュします。上記の例と同様に、車のモデルは車の所有者専用の Flyweight に保存されます。オーナー車両データをデータベースに追加するとき、Flyweight Factory はまず、このモデルの車のデータが Flyweight に既に存在するかどうかを確認し、存在する場合はそれを直接返し、存在しない場合はこのモデルを作成します。

行動タイプ  

行動パターンは、オブジェクト間の効率的なコミュニケーションと責任の委任に関与します。

  • イテレータパターン

イテレータ パターンは、コレクションの基礎となる表現 (リスト、スタック、ツリーなど) を公開することなく、コレクション内のすべての要素を走査できる動作設計パターンです。

現実的な例を挙げると、北京に旅行する場合、紫禁城、万里の長城、天安門広場、頤和園、北京大学、清華大学がこの旅行の目的地となり、目的地のコレクションを作成できます。 。1 つ目は自分の希望順にプレイする方法、2 つ目はブロガーが推奨するツアーの順序に従う方法、3 つ目はツアーグループに登録し、主催者が手配した順序でプレイする方法です。ツアーグループ。上記の 3 つの選択肢は、宛先コレクションの反復子です。

イテレータパターンの構造:

8e246b51009d3ea7f97c601bf05d3d2d.png

コード例:

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
}

これを理解する最も簡単な方法は、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...
  • メディエーターパターン

Mediator パターンは、オブジェクト間の煩雑な依存関係を軽減する動作設計パターンです。このパターンでは、オブジェクト間の直接の対話が制限され、メディエーター オブジェクトを介して連携することが強制されます。

現実的な例を挙げると、航空機は相互に直接通信するのではなく、タワーと通信して、ルートの交差や反復によって引き起こされる可能性のある航空機間の衝突を回避します。もちろん、実際には他にも多くの例がありますが、仲介モデルは、実際には私たちが文字通り理解している仲介の意味です。

メディエーター パターンの構造:

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.

プログラム コンポーネントは、特別な中間オブジェクトを通じて間接的に通信し、コンポーネント間の依存関係を減らすという目的を達成します。

  • 訪問者のパターン

Visitor パターンは、アルゴリズムを操作対象のオブジェクトから分離する動作設計パターンです。

現実的な例で言えば、不動産営業マンはあらゆるタイプの人に売ることができ、お金持ち相手なら別荘を売りますが、普通の高級物件も売ります。

訪問者パターンの構造:

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.
  • メモモード

memento パターンは、オブジェクトの実装の詳細を公開することなく、オブジェクトの以前の状態を保存および復元できるようにする動作設計パターンです。内部構造は、スナップショットに保存されたデータには影響しません。

メモのスキーマ構造:

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: 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
  • 戦略パターン

ストラテジ パターンは、一連のアルゴリズムの定義を可能にし、各アルゴリズムを独立したクラスに配置して、アルゴリズムのオブジェクトを相互に置き換えることができる動作設計パターンです。

現実的な例で言えば、電車に乗る場合、駅に到着するための戦略にはバス、地下鉄、タクシーなどがあり、予算や時間などの要因に基づいてこれらの戦略の 1 つを選択できます。

戦略パターンの構造:

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,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
  • テンプレートメソッドパターン

テンプレート メソッド パターンは、基本クラスのアルゴリズムのフレームワークを定義する動作設計パターンであり、サブクラスが構造を変更せずにアルゴリズムの特定のステップを書き換えられるようにします。

現実的な例を挙げると、田舎の自作住宅には、固定のテンプレート手法(基礎の設置 -> 本体の建設 -> キャップの設置 -> 水道と電気の敷設 -> 装飾など)が存在しますが、いくつかの拡張機能を提供できます。ほとんどの住宅所有者は、装飾スタイルなど、完成した家の細部を調整して、完成した家を異なったものにします。

テンプレートメソッドのパターン構造:

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 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 パターンは、ハンドラーのチェーンに沿ってリクエストを送信できるようにする動作設計パターンです。リクエストを受信した後、各プロセッサはリクエストを処理するか、チェーン内の次のプロセッサにリクエストを渡すことができます。

責任連鎖パターン構造:

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 > 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.
  • コマンドモード

コマンド パターンは、リクエストを、リクエストに関連するすべての情報を含む単一のオブジェクトに変換する動作設計パターンです。変換ではメソッドをパラメータ化し、さまざまなリクエストに応じてリクエストの実行を遅らせたり、リクエストをキューに入れたり、取り消し可能な操作を実装したりできます。

コマンドモードの構造:

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

チーム紹介

私たちはビッグタオバオテクノロジーマーケティングおよびプラットフォーム戦略テクノロジーマーケティング製品チームであり、主に淘宝好価格、百億補助金、樹華素安、天天特別セールなどの淘宝網のコア製品およびさまざまな大規模マーケティング会場活動を担当しています。 、私たちはフロントエンドインテリジェンスでもあります グローバリゼーションの先駆者には、Ark、Qianfan、imgcook、スマート UI などのさまざまな技術製品があります。

¤ 拡張読書 ¤

3DXR技術 | 端末技術 | オーディオおよびビデオ技術

サーバーテクノロジー | 技術品質 | データアルゴリズム

Supongo que te gusta

Origin blog.csdn.net/Taobaojishu/article/details/131777909
Recomendado
Clasificación