久しぶりのデザパタです、今回はFactory Methodです。
Factory Methodとは
Template Methodパターンをインスタンス生成に適応したものです。
Factory(工場)はインスタンスを作る役割を果たし、作り方の枠組みを示す親クラス(抽象クラス)と実際に特定のクラスを定めて作る子クラスに分けて作成します。
「工場」という抽象クラスを継承してお菓子を作る「お菓子工場とか」を作ってやる感じです。
サンプル
IDカードを作るIDカード工場を作成します。
IDカードは使用するとその旨を伝える文字列をコンソールに表示します。
登場人物
- Factoryクラス
- 実際にIDカードを作る工場を作る元になる抽象クラス
- Productクラス
- Factoryに作られるクラスの元になる抽象クラス
- IdCardFactoryクラス
- Factoryクラスを継承して実際にIDカードを作るクラス
- IdCard
- IDカードを示すクラス
ソース
Factory
生成するために呼ぶメソッドcreate()
を実装し、createメソッドが呼ぶ実際に作るメソッドcreateProduct()
を抽象メソッドとして定義します。
import { Product } from './product'; export abstract class Factory { create(owner: string): Product { var p: Product = this.createProduct(owner); this.registerProduct(p); return p; } protected abstract createProduct(owner: string): Product; protected abstract registerProduct(product: Product): void; }
Product
Factoryによって作られる「製品」を表したクラスです。実際に製品を使う際に呼ばれるuse()
という抽象メソッドを定義しています。
export abstract class Product { public abstract use(): void; }
IdCardFactory
実際にIdCardクラスを作る工場です。持ち主をの名前を受け取ってIdカードを作って返しています。
import { IdCard } from './idCard'; import { Factory } from './factory'; import { Product } from './product'; export class IdCardFactory extends Factory { public readonly owners: Array<string> = []; protected createProduct(owner: string): Product { return new IdCard(owner); } protected registerProduct(product: Product): void { this.owners.push((<IdCard>product).owner); }
IdCard
import { Product } from './product'; export class IdCard extends Product { public readonly owner: string; constructor(owner: string){ super(); console.log(owner + "さんのカードを作ります。"); this.owner = owner; } public use(): void { console.log(this.owner + "さんのカードを使います。"); } }
Main
Factoryを作成してIDカードを作らせた後、それらのカードを使用しています。
その際IdCardクラスの作り方は知らなくても良くてFactoryに任せておけば良いのが分かります。
作られるのがProductクラスを継承したもので、作るのがFactoryを継承したものなので、サブクラスの中身を知らなくても使える状態になってます。
import { IdCardFactory } from './idCardFactory'; import { Factory } from "./factory"; import { Product } from './product'; var factory: Factory = new IdCardFactory(); var card1: Product = factory.create("やまだ"); var card2: Product = factory.create("すずき"); var card3: Product = factory.create("さとう"); card1.use(); card2.use(); card3.use();
実行結果
$ ts-node "factory_method/main.ts" やまださんのカードを作ります。 すずきさんのカードを作ります。 さとうさんのカードを作ります。 やまださんのカードを使います。 すずきさんのカードを使います。 さとうさんのカードを使います。
Template Methodパターンもそうですが、抽象クラスを上手く使うことでサブクラスの入れ替えを容易にして保守性を高めるというところがポイントだと思いました。抽象クラス大活躍です。