ちゃんなるぶろぐ

エンジニア5年生🧑‍💻 オライリーとにらめっこする毎日。

【5分でわかる🏭】TypeScriptでDesign Pattern〜Factory Method Pattern〜

どうも、ちゃんなるです!

今回は、Factory Method PatternをTypeScriptのサンプルコードを用いて紹介します🖐️

概要

これは「オブジェクトの生成をサブクラスに委譲するデザインパターン」です。

インスタンスの生成を専用のファクトリーメソッドに分離することで、コードの再利用性や拡張性を高めることができます🏭

3つポイントを挙げると

  1. Template Method Patternインスタンス生成の場面に適用したもの
  2. スーパークラス側でインスタンスの作り方を定め、具体的な肉付けはサブクラス側で行う
  3. インスタンス生成の枠組み(フレームワーク)と実際のインスタンス生成のクラスとを分けて考えることができる

と言えます。Template Method Patternについては別の記事で紹介しているのでぜひ👀

chan-naru.hatenablog.com

実現方法

動物のインスタンスを生成する設計を例にします🦁

下記の要素を持たせます。

  • AnimalType列挙型: 使用可能な動物の種類を定義、新たな動物クラス追加時にタイプ追加
  • Animal抽象クラス: 動物の基本機能定義、具象クラスはAnimalクラス継承、独自のspeakメソッド実装
  • 具象クラス: Animalクラス継承、特定の動物の振る舞い実装、鳴き声をコンソールに出力
  • AnimalFactoryクラス: Factory Method Patternの中心、createAnimalメソッドでAnimalTypeを受け取り、対応する動物のインスタンス生成

クラス図

サンプルコードのクラス図:Mermaid Live Editorで作成

サンプルコード

以下に、動物のインスタンスを生成するTypeScriptのサンプルコードを示します。

enum AnimalType {
  Dog,
  Cat,
  Fish,
  Bird,
  Lion,
}

abstract class Animal {
  abstract speak(): void;
}

class Dog extends Animal {
  speak() {
    console.log("ワンワン!");
  }
}

class Cat extends Animal {
  speak() {
    console.log("ニャー!");
  }
}

class Fish extends Animal {
  speak() {
    console.log("ブクブク!");
  }
}

class Bird extends Animal {
  speak() {
    console.log("ピーチク!");
  }
}

class Lion extends Animal {
  speak() {
    console.log("ガオー!");
  }
}

class AnimalFactory {
  createAnimal(type: AnimalType): Animal {
    switch (type) {
      case AnimalType.Dog:
        return new Dog();
      case AnimalType.Cat:
        return new Cat();
      case AnimalType.Fish:
        return new Fish();
      case AnimalType.Bird:
        return new Bird();
      case AnimalType.Lion:
        return new Lion();
      default:
        throw new Error("Invalid animal type");
    }
  }
}

クライアント(使う側)のコードを見てみましょう👩‍💻

const factory = new AnimalFactory();

const dog = factory.createAnimal(AnimalType.Dog);
dog.speak(); // ワンワン!

const cat = factory.createAnimal(AnimalType.Cat);
cat.speak(); // ニャー!

const fish = factory.createAnimal(AnimalType.Fish);
fish.speak(); // ブクブク!

const bird = factory.createAnimal(AnimalType.Bird);
bird.speak(); // ピーチク!

const lion = factory.createAnimal(AnimalType.Lion);
lion.speak(); // ガオー!

このように設計することで、新しく動物クラスを追加したくなった際は、AnimalType列挙型に新たな動物タイプを追加し、AnimalFactoryクラスcreateAnimalメソッド内のswitch文を拡張するだけで対応できます。

簡単に新しい動物クラスのインスタンスを生成できる(拡張性が高い)わけですね👍

Factory Method Patternの使い道

以下のような場面で役立ちます。

  • オブジェクト生成時に必要なロジックが複雑な場合
  • 生成するオブジェクトの種類が多岐にわたる場合
  • オブジェクト生成に関するコードを集約し、可読性やメンテナンス性を向上させたい場合

組み合わせられるデザインパターン

Factory Method Patternは、他のデザインパターンと組み合わせることで、さらに効果を発揮します。

例えば、以下のパターンと組み合わせることができます。

  • Abstract Factory Pattern: オブジェクトの生成だけでなく、関連するオブジェクトの生成も一元化することができる

chan-naru.hatenablog.com

  • Singleton Pattern: Factory Method Patternで生成されるオブジェクトを一意にすることができる

chan-naru.hatenablog.com

まとめ

Factory Method Patternは、オブジェクトの生成をサブクラスに委譲することで、コードの再利用性や拡張性を高めるデザインパターンです🏭

TypeScriptで実装する際は、抽象クラスと具象クラスを使って簡単に実現できます。

さらに、switch文を用いることで、条件分岐部分のコードがシンプルになり、追加や変更が容易になります。

ぜひお試しください〜✌️

参考文献

www.oreilly.com

en.wikipedia.org