Design Patterns 第2回 生成に関するパターン Factory
前回のリンク
生成に関するパターン
Factory
単一の派生クラス群からインスタンスを生成するユーティリティクラス
ファクトリーデザインパターンは共通の基本型から派生しているたくさんの違うタイプのオブジェクトの生成をするときに役立つ。オブジェクトを生成するメソッドとしてファクトリーメソッドは定義され、そのサブクラスはオーバーライドして生成される派生型を指定する。
そのようにして実行時にファクトリーメソッドは望んだオブジェクトの記述(たとえば、ユーザーからの入力文字列)を渡され、そのオブジェクトの新しいインスタンスへの基底クラスのポインターを返す。このパターンは良く設計されたインターフェースが基本クラスに使われるときにもっともうまく機能する。返されたオブジェクトをキャストする必要はない。
問題
いくつかの設定やアプリケーションのパラメーターに基づいて、どんなオブジェクトを生成すべきかを実行時に決定したい。けれどもソースコードを書いたときにどんなクラスがインスタンス化されるかを知らない。
解決法
オブジェクトを生成するインターフェースを定義する。 しかし、サブクラスにどのクラスをインスタンス化するかを決定させる。ファクトリーメソッドはサブクラスのインスタンス化を先延ばしにする。
次の例ではファクトリーメソッドはラップトップのオブジェクトやデスクトップのオブジェクトを実行時に生成するのに使われる。Computerという抽象基本クラス(もしくはインターフェース)と、LaptopやDesktopという派生クラスを定義してやってみよう。実際のComputerFactoryクラスはComputerクラスを返し、オブジェクトの実際の記述を与えられる。
ソースコード
実行結果
$ ./factory.exe ハイバーネート解除します(*´Д`) ハイバーネートします(´・ω・`) 起動しますた(*´ω`*) 電源切ります(;´Д`)
このデザインのいいところを解説しよう。まず、コンパイルでいいことがある。もしComputerインターフェースをファクトリーと一緒に、別のヘッダーファイル(.h)に移動すれば、create()関数の実装を別の実装ファイル(.cpp)に移動することができる。そこではcreate()の実装ファイルは派生クラスだけがわかっていればよい。そのようにして、Computerの派生クラスに変更がなされても、新しくComputerのサブタイプが追加されても、create()の実装ファイルだけを再コンパイルするだけだ。ファクトリーを使う側が、アプリケーションの寿命を通して一貫性があるインターフェースだけに気を使っていればよい。もちろんクラスを追加する必要があったり、また、ユーザーがユーザーのインターフェースを介してオブジェクトを要求しているとしても、追加のコンピュータの型をサポートするために、ファクトリーを呼んでいるソースコードを変更する必要はない。ファクトリーを使っているソースコードは単に新しいEnum型の整数を渡すだけで、全般的に新しい型を扱うことができる。ゲームプログラミングを想像すれば、将来的に新しいタイプの敵キャラを追加したいと思ったところで、それぞれが違うAIの関数を持ち、別々にアップデートすることができる。ファクトリーメソッドを使うことでプログラミングの管理側が、依存性や実際の敵のタイプを知らなくても敵キャラを生成するのにファクトリーを呼ぶことができる。そこで、後で開発に加わった人も、新しいAIと新しい描画関数を持った敵キャラを作れるし、ファクトリーにそれを加えて、名前で敵を問い合わせて、ファクトリーを呼ぶ水準のものを作成できる。レベルのXMLの記述とこの方法を組み合わせて、開発者はプログラムを再コンパイルすることなく新しいレベルを作成することができるかもしれない。
これができるのはオブジェクトの使い方からオブジェクトの生成を分離するおかげだ。