💡gntk.dev

【Factory Method】GoFのデザインパターンを学ぶ

GoFのデザインパターンをTypeScriptで写経しました。今回はFactory Methodパターンです。

post-cover

概要

Template Methodをインスタンスの生成の場面に適用したものを、Factory Methodパターンと呼ぶ。スーパークラスでインスタンス生成の大枠を決め、サブクラスで実際のインスタンス生成を実装する。

サンプルプログラム

結城浩先生の増補改訂版Java言語で学ぶデザインパターン入門のJavaプログラムをTypeScriptで写経した。

工場(factory)を用意し、factory.createで製品(IDCard)のインスタンスを生成するプログラム。cardはcard.useメソッドを持つ。 使用イメージは以下の通り。

import { Factory } from 'factoryMethod/framework/factory';
import { Product } from 'factoryMethod/framework/product';
import { IDCardFactory } from 'factoryMethod/idcard/IDCardFactory';

describe('Factory Method', () => {
  const factory: Factory = new IDCardFactory();
  const card: Product = factory.create('結城浩');

  test('use', () => {
    expect(card.use()).toEqual('結城浩のカードを使います');
  });

  test('owners', () => {
    const owners = (factory as IDCardFactory).getOwners();
    expect(owners).toEqual(['結城浩']);
  });
});

製品の抽象クラスとしてproduct、製品のインスタンスを作成する工場の抽象クラスとしてfactoryを、それぞれframeworkとして実装する。

framework

product.ts

export abstract class Product {
  public abstract use(): string;
}

productは、とりあえずuseを持つことだけを決めておく。useで何をするかはサブクラスによる実装次第。

factory.ts

import { Product } from 'factoryMethod/framework/product';

export abstract class Factory {
  public create(owner: string): Product {
    const p: Product = this.createProduct(owner);
    this.registerProduct(p);
    return p;
  }

  protected abstract createProduct(owner: string): Product;
  protected abstract registerProduct(product: Product): void;
}

factoryは、createによってProductのインスタンスを生成・登録・返却する処理をすることを決めておく。生成・登録の具体的な処理はサブクラスによる実装次第。

IDCard

productの実装として、IDCardを実装する。

IDCard.ts

import { Product } from 'factoryMethod/framework/product';

export class IDCard extends Product {
  private owner: string;

  constructor(owner: string) {
    super();
    console.log(`${owner}のカードを作ります`);
    this.owner = owner;
  }

  public use(): string {
    return `${this.owner}のカードを使います`;
  }
  public getOwner(): string {
    return this.owner;
  }
}

IDCardはowner(誰のカードか)の情報を持ち、useでそのカードの持ち主がわかる文字列を返すようにした。

IDCardFactory.ts

import { Factory } from 'factoryMethod/framework/factory';
import { Product } from 'factoryMethod/framework/product';
import { IDCard } from 'factoryMethod/idcard/IDCard';

export class IDCardFactory extends Factory {
  private owners: string[] = [];
  protected createProduct(owner: string): Product {
    return new IDCard(owner);
  }
  protected registerProduct(product: Product): void {
    this.owners.push((product as IDCard).getOwner());
  }
  public getOwners(): string[] {
    return this.owners;
  }
}

IDCardのインスタンスを生成するIDCardFactory。生成したIDCardの持ち主の情報を持つ配列ownersを持つ。create(抽象クラスfactoryに定義してある)したときにIDCardのインスタンスを生成、ownersの配列にpushすることで登録する処理を行う。

考察・感想

getOwnersをテストするためにfactoryを型アサーション使ってIDCardFactoryにした。FactoryのままだとgetOwnersが実装されてなくて使えないので。

あと、Javaだとpackageというのがあるから、package外からnewできないみたいなのができるっぽいけど、TypeScriptだとそういうスコープはないのだろうか。

© 2021 gntk.dev