知っておくべき 5 つの TypeScript デザイン パターン

青灯夜游
リリース: 2020-11-20 17:36:30
転載
9332 人が閲覧しました

この記事では、5 つの TypeScript デザイン パターンを紹介します。一定の参考値があるので、困っている友達が参考になれば幸いです。

知っておくべき 5 つの TypeScript デザイン パターン

# デザイン パターンは、開発者が問題を解決するのに役立つテンプレートです。この本ではあまりにも多くのパターンが取り上げられており、多くの場合、異なるニーズを対象としています。ただし、これらは 3 つの異なるグループに分類できます。 <p></p>
  • 構造パターン さまざまなコンポーネント (またはクラス) 間の関係を扱い、新しい機能を提供する新しい構造を形成します。構造パターンの例には、CompositeAdapter、および Decorator があります。
  • 動作パターンコンポーネント間の共通の動作を独立したエンティティに抽象化します。行動パターンの例としては、コマンド、戦略、そして私の個人的なお気に入りである Observer Pattern があります。
  • 作成モードはクラスのインスタンス化に焦点を当てており、新しいエンティティの作成が容易になります。ファクトリ メソッド、シングルトン、抽象ファクトリについて話しています。

シングルトン パターン

シングルトン パターンは、おそらく最も有名なデザイン パターンの 1 つです。これは、クラスのインスタンス化を何度試みても、使用できるインスタンスは 1 つだけであることが保証されるため、作成パターンです。 <p></p>データベース接続の処理などはシングルトン モードで実行できます。これは、ユーザー要求ごとに再接続することなく、一度に 1 つだけ処理したいためです。 <p></p>
class MyDBConn {
  protected static instance: MyDBConn | null = null
  private id:number = 0

  constructor() {
    this.id = Math.random()
  }

  public getID():number {
    return this.id
  }

  public static getInstance():MyDBConn {
    if (!MyDBConn.instance) {
      MyDBConn.instance = new MyDBConn()
    }
    return MyDBConn.instance
  }
}

const connections = [
  MyDBConn.getInstance(),
  MyDBConn.getInstance(),
  MyDBConn.getInstance(),
  MyDBConn.getInstance(),
  MyDBConn.getInstance()
]

connections.forEach( c => {
    console.log(c.getID())
})
ログイン後にコピー
クラスを直接インスタンス化することはできませんが、

getInstance メソッドを使用すると、複数のインスタンスが存在しないようにできます。上の例では、データベース接続をラップする疑似クラスがこのパターンからどのように恩恵を受けるかがわかります。

この例は、

getInstance メソッドを何度呼び出しても、接続は常に同じであることを示しています。

上記の実行結果: <p></p>
0.4047087250990713
0.4047087250990713
0.4047087250990713
0.4047087250990713
0.4047087250990713
ログイン後にコピー

ファクトリー モード

##ファクトリー モード

は、Single と同様の作成モードです。ケースモード 同じです。ただし、このパターンは対象のオブジェクトに直接作用するのではなく、オブジェクトの作成を管理するだけです。 説明: 移動する乗り物をシミュレートするコードを作成するとします。車、自転車、飛行機など、さまざまな種類の乗り物があります。モバイル コードは、各

vehicle

クラスにカプセル化する必要があります。ただし、呼び出し中の move メソッドのコードは汎用的なものにすることができます。 ここでの質問は、オブジェクトの作成をどのように処理するかということです? 3 つのメソッドを持つ単一の

creator

クラス、またはパラメーターを取るメソッドが存在する可能性があります。いずれの場合も、より多くの vehices の作成をサポートするためにそのロジックを拡張するには、同じクラスを継続的に成長させる必要があります。 ただし、ファクトリ メソッド パターンを使用することにした場合は、次の操作を行うことができます。

<p></p>

知っておくべき 5 つの TypeScript デザイン パターン次に、新しいメソッドを作成します。 object required コードは、車両タイプごとに 1 つずつ、新しいクラスにカプセル化されます。これにより、将来車両を追加する必要がある場合、既存のクラスを変更することなく、新しいクラスを追加するだけで済みます。

これを

TypeScript

を使用してどのように実現できるかを見てみましょう: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">interface Vehicle {     move(): void } class Car implements Vehicle {     public move(): void {         console.log(&quot;Moving the car!&quot;)     } } class Bicycle implements Vehicle {     public move(): void {         console.log(&quot;Moving the bicycle!&quot;)     } } class Plane implements Vehicle {     public move(): void {         console.log(&quot;Flying the plane!&quot;)     } } // VehicleHandler 是“抽象的”,因为没有人会实例化它instantiate it // 我们要扩展它并实现抽象方法 abstract class VehicleHandler {     // 这是真正的处理程序需要实现的方法     public abstract createVehicle(): Vehicle      public moveVehicle(): void {         const myVehicle = this.createVehicle()         myVehicle.move()     } }  class PlaneHandler extends VehicleHandler{     public createVehicle(): Vehicle {         return new Plane()     } } class CarHandler  extends VehicleHandler{     public createVehicle(): Vehicle {         return new Car()     } } class BicycleHandler  extends VehicleHandler{     public createVehicle(): Vehicle {         return new Bicycle()     } } /// User code... const planes = new PlaneHandler() const cars = new CarHandler() planes.moveVehicle() cars.moveVehicle()</pre><div class="contentsignin">ログイン後にコピー</div></div>上記は大量のコードですが、上の図を使用して理解できます。基本的に最終的に重要なのはカスタム ハンドラーです。ここでは作成者ではなくハンドラーと呼びます。これは、オブジェクトを作成するだけでなく、オブジェクトを使用するロジック (moveVehicle メソッド) も備えているためです。

このパターンの利点は、新しい

vehicle

タイプを追加する場合、その vehicle クラスとそのハンドラー プログラム クラスを追加するだけで済むことです。他のクラスの LOC を増加させることなく。

Observer Pattern

すべてのパターンの中で、私のお気に入りは

Observer Pattern

です。これは、実装できる型の動作のためです。 どのように機能するのでしょうか? 基本的に、このパターンは、観察されるエンティティの状態の変化に反応する一連のオブザーバー オブジェクトがあることを示します。これを達成するために、観察される側で変更が受信されると、そのメソッドの 1 つを呼び出して観察者に通知する責任があります。

実際には、このパターンの実装は比較的簡単です。コードをざっと見てから確認してみましょう。

type InternalState = {
  event: String
}

abstract class Observer {
  abstract update(state:InternalState): void
}

abstract class Observable {
  protected observers: Observer[] = []
  protected state:InternalState = { event: ""}

  public addObserver(o: Observer):void {
    this.observers.push(o)
  }

  protected notify () {
    this.observers.forEach(o => o.update(this.state))
  }
}


class ConsoleLogger extends Observer  {

    public update(newState: InternalState) {
        console.log("New internal state update: ", newState)
    }
}

class InputElement extends Observable {

    public click():void {
        this.state = { event: "click" }
        this.notify()
    }

}

const input = new InputElement()
input.addObserver(new ConsoleLogger())

input.click()
ログイン後にコピー

ご覧のとおり、2 つの抽象クラスを使用して # を定義できます。 ##Observer

Observable エンティティの変更に反応するオブジェクトを表します。上記の例では、クリックされた InputElement エンティティ (フロントエンドに HTML 入力フィールドがある場合と同様) と、コンソールにログを記録する ConsoleLogger があると想定しています。起こったことすべて。 このパターンの利点は、内部コードをいじらずに Observable

の内部状態を理解し、それに反応できることです。他のアクションを実行するオブザーバー (特定のイベントに反応するオブザーバーも含む) を引き続き追加し、各通知に対して何を行うかをコードに決定させることができます。 <p></p>

装饰模式

装饰模式试图在运行时向现有对象添加行为。 从某种意义上说,我们可以将其视为动态继承,因为即使没有创建新类来添加行为,我们也正在创建具有扩展功能的新对象。

这样考虑:假设我们拥有一个带有move方法的Dog类,现在您想扩展其行为,因为我们想要一只超级狗和一只可以游泳的狗。

通常,我们需要在 Dog 类中添加move 行为,然后以两种方式扩展该类,即SuperDogSwimmingDog类。 但是,如果我们想将两者混合在一起,则必须再次创建一个新类来扩展它们的行为,但是,有更好的方法。

组合让我们可以将自定义行为封装在不同的类中,然后使用该模式通过将原始对象传递给它们的构造函数来创建这些类的新实例。 让我们看一下代码:

abstract class Animal {

    abstract move(): void
}

abstract class SuperDecorator extends Animal {
    protected comp: Animal
    
    constructor(decoratedAnimal: Animal) {
        super()
        this.comp = decoratedAnimal
    }
    
    abstract move(): void
}

class Dog extends Animal {

    public move():void {
        console.log("Moving the dog...")
    }
}

class SuperAnimal extends SuperDecorator {

    public move():void {
        console.log("Starts flying...")
        this.comp.move()
        console.log("Landing...")
    }
}

class SwimmingAnimal extends SuperDecorator {

    public move():void {
        console.log("Jumps into the water...")
        this.comp.move()
    }
}


const dog = new Dog()

console.log("--- Non-decorated attempt: ")
dog.move()

console.log("--- Flying decorator --- ")
const superDog =  new SuperAnimal(dog)
superDog.move()

console.log("--- Now let's go swimming --- ")
const swimmingDog =  new SwimmingAnimal(dog)
swimmingDog.move()
ログイン後にコピー

注意几个细节:

  • 实际上,SuperDecorator类扩展了Animal类,与Dog类扩展了相同的类。 这是因为装饰器需要提供与其尝试装饰的类相同的公共接口。
  • SuperDecorator类是abstract ,这意味着并没有使用它,只是使用它来定义构造函数,该构造函数会将原始对象的副本保留在受保护的属性中。 公共接口的覆盖是在自定义装饰器内部完成的。
  • SuperAnimalSwimmingAnimal是实际的装饰器,它们是添加额外行为的装饰器。

进行此设置的好处是,由于所有装饰器也间接扩展了Animal类,因此如果你要将两种行为混合在一起,则可以执行以下操作:

const superSwimmingDog =  new SwimmingAnimal(superDog)

superSwimmingDog.move()
ログイン後にコピー

Composite(组合)

关于Composite模式,其实就是组合模式,又叫部分整体模式,这个模式在我们的生活中也经常使用。

比如编写过前端的页面,肯定使用过<p></p>等标签定义一些格式,然后格式之间互相组合,通过一种递归的方式组织成相应的结构,这种方式其实就是组合,将部分的组件镶嵌到整体之中。

关于此模式的有趣之处在于,它不是一个简单的对象组,它可以包含实体或实体组,每个组可以同时包含更多组,这就是我们所说的树。

看一个例子:

interface IProduct {
  getName(): string
  getPrice(): number
}

class Product implements IProduct {
  private price:number
  private name:string

  constructor(name:string, price:number) {
    this.name = name
    this.price = price
  }

  public getPrice():number {
    return this.price
  }

  public getName(): string {
    return this.name
  }
}

class Box implements IProduct {

    private products: IProduct[] = []
    
    contructor() {
        this.products = []
    }
    
    public getName(): string {
        return "A box with " + this.products.length + " products"
    } 
    
    add(p: IProduct):void {
        console.log("Adding a ", p.getName(), "to the box")
        this.products.push(p)
    }

    getPrice(): number {
        return this.products.reduce( (curr: number, b: IProduct) => (curr + b.getPrice()),  0)
    }
}

//Using the code...
const box1 = new Box()
box1.add(new Product("Bubble gum", 0.5))
box1.add(new Product("Samsung Note 20", 1005))

const box2 = new Box()
box2.add( new Product("Samsung TV 20in", 300))
box2.add( new Product("Samsung TV 50in", 800))

box1.add(box2)

console.log("Total price: ", box1.getPrice())
ログイン後にコピー

在上面的示例中,我们可以将product 放入Box中,也可以将Box放入其他Box中,这是组合的经典示例。因为我们要实现的是获得完整的交付价格,因此需要在大box里添加每个元素的价格(包括每个小box的价格)。

上面运行的结果:

Adding a  Bubble gum to the box
Adding a  Samsung Note 20 to the box
Adding a  Samsung TV 20in to the box
Adding a  Samsung TV 50in to the box
Adding a  A box with 2 products to the box
Total price:  2105.5
ログイン後にコピー

因此,在处理遵循同一接口的多个对象时,请考虑使用此模式。 通过将复杂性隐藏在单个实体(组合本身)中,您会发现它有助于简化与小组的互动方式。

今天的分享就到这里了,感谢大家的观看,我们下期再见。

原文地址:https://blog.bitsrc.io/design-patterns-in-typescript-e9f84de40449

作者:Fernando Doglio

译文地址:https://segmentfault.com/a/1190000025184682

更多编程相关知识,请访问:编程课程!!

以上が知っておくべき 5 つの TypeScript デザイン パターンの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:segmentfault.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート