TypeScript デザインパターン10個

デザインパターンは、特定の問題を解決するための検証済みのソリューションです。それらは再利用可能で、コードの可読性と効率を向上させることができます。この記事では、TypeScriptでよく使用される10種類のデザインパターンについて説明します。

目次

  1. Factory パターン
  2. Singleton パターン
  3. Observer パターン
  4. Decorator パターン
  5. Strategy パターン
  6. Facade パターン
  7. Command パターン
  8. Adapter パターン
  9. Prototype パターン
  10. Composite Pattern

1. Factory Pattern

Factoryパターナは、クラスが直接オブジェクトを作成する代わりに、ファクトリメソッドを通じてそのオブジェクトを作成する方法です。

interface Product {
    operation(): string;
}

class ConcreteProductA implements Product {
    public operation(): string {
        return '{Result of the ConcreteProductA}';
    }
}

class ConcreteProductB implements Product {
    public operation(): string {
        return '{Result of the ConcreteProductB}';
    }
}

abstract class Creator {
    public abstract factoryMethod(): Product;

    public someOperation(): string {
        const product = this.factoryMethod();
        return `Creator: The same creator's code has just worked with ${product.operation()}`;
    }
}

class ConcreteCreatorA extends Creator {
    public factoryMethod(): Product {
        return new ConcreteProductA();
    }
}

2. Singleton Pattern

Singletonパーツは一度に一つしか存在しないオブジェクトが必要な場合に使用されます。

class Singleton {
  private static instance: Singleton;

  private constructor() {}

  static getInstance() : Singleton{
      if (!Singleton.instance) 
          Singleton.instance = new Singleton();
      return Singleton.instance;
  }

}

3. Observer Pattern

Observerパーツはあるオブジェクト(サブジェクト)の状態変化を他の依存するオブジェクト(観察者)に通知することを可能にします。

interface Observer {
    update(subject: Subject): void;
}

class ConcreteObserver implements Observer {
    public update(subject: Subject): void {
        console.log('ConcreteObserver: Reacted to the event.');
    }
}

class Subject {
    private observers: Observer[] = [];

    public subscribe(observer: Observer): void {
        const isExist = this.observers.includes(observer);
        if (isExist) return;
        this.observers.push(observer);
    }

    public unsubscribe(observer: Observer): void {
        const observerIndex = this.observers.indexOf(observer);
        if (observerIndex === -1) return;

        this.observers.splice(observerIndex, 1);
    }

}

4. Decorator Pattern

Decoratorパターンは、既存のオブジェクトに新しい機能を動的に追加します。

interface Component{
   operation(): string;
}

class ConcreteComponent implements Component{
   operation(): string{
      return 'ConcreteComponent';
   }
}

class Decorator implements Component{
   private component : Component;

   constructor(component : Component){
      this.component = component;
   }

   operation() : string{
      return `Decorator(${this.component.operation()})`;
   }
}

5. Strategy Pattern

Strategyパターンは、アルゴリズムのセットを定義し、それぞれをカプセル化して交換可能にします。これにより、アルゴリズムが独立してクライアントから進化できます。

interface Strategy {
  doAlgorithm(data: string[]): string[];
}

class Context {
  private strategy: Strategy;

  constructor(strategy: Strategy) {
      this.strategy = strategy;
  }

  public setStrategy(strategy: Strategy) {
      this.strategy = strategy;
  }

}

6. Facade Pattern

Facadeパターンは複雑なシステムの一部分だけを取り扱うインターフェースを提供するものです。

class Facade {

private subsystem1 : Subsystem1;

private subsystem2 : Subsystem2;

constructor(subsystem1?: Subsystem1, subsystem2?: Subsystem2){
if(subsystem1 == null)
this.subsystem1 = new SubSystem1();
else
this.subsytem1=subsytem1;

if(subsystem2 == null)
this.subsytem2=new SubSystem2();
else
this.subsytem2=subsytem2;
}
operation():string{

let result:string='';
result += 'Facade initializes subsystems:\n';
result += this._subsystem1.operation();
result += '\n';
result += 'Facade orders subsystems to perform the action:\n';
result += this._subsystem.operation();
result += '\n';
return result;
}
}

7. Command Pattern

Commandパターンは要求自体をオブジェクトとしてカプセル化し、パラメータを変更したり、キューに入れたり、ログに記録したりすることができます。

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 Invoker {
    private onStart: Command;
    private onFinish: Command;

    public setOnStart(command: Command): void {
        this.onStart = command;
    }

}

8. Adapter Pattern

Adapterパターンは、互換性のないインターフェースを持つクラス間で協力するためのパターンです。

interface Target {
  request(): string;
}

class Adaptee {
  specificRequest(): string {
      return '.eetpadA eht fo roivaheb laicepS';
  }
}

class Adapter implements Target{
  private adaptee : Adaptee;

  constructor(adaptee : Adaptee){
      this.adaptee = adaptee;
  }

  request() : string{
      const result = this.adaptee.specificRequest().split('').reverse().join('');
      return `Adapter: (TRANSLATED) ${result}`;
   }
}

9. Prototype Pattern

Prototypeパターンは、新しいオブジェクトを既存のオブジェクトから作成するためのパターンです。

interface Prototype{
   clone() : Prototype;
   toString() : string; 
}

class ConcretePrototype1 implements Prototype{

   private date : Date;

   constructor(date? : Date){
       if(date == null)
           this.date = new Date();
       else
           this.date = date; 
   }

   clone() : Prototype{
       return new ConcretePrototype1(new Date(this.date.getTime()));
   }

}

10. Composite Pattern

Composite パターンは、オブジェクト群を単一のオブジェクトと同じように扱うことができる構造を定義します。これにより、客戶端から個々の要素とコレクションを区別することなく扱うことが可能になります。

abstract class Component {

abstract operation():string;

add(component:Component):void{}

remove(component:Component):void{}

getChild(n:number):Component{return null;}

}

class Leaf extends Component{

private name:string;

constructor(name:string){
this.name=name;
}

operation():string{
return 'Leaf';
}
}


class Composite extends Component{

protected children:Component[]=[];

public add(component:Component):void{
this.children.push(component);
}

public remove(component:Component):void{...}

public operation():string{...}
}

これらすべてのデザインパターンはTypeScriptでのソフトウェア開発を容易にします。これらのパターンを理解し、適切な場所で適用することは、コードの再利用性、可読性、および保守性を大幅に向上させることができます。