4. FactoryMethod パターン
- 2012/04/26 一部修正しました
- 4.1 FactoryMethodパターンとは
- 4.2 サンプルケース
- 4.3 FactoryMethod パターンのまとめ
4.1 FactoryMethodパターンとは
第4章では、FactoryMethod パターンを紹介します。FactoryMethod パターンは、オブジェクトの生成方法に一工夫加えることで、より柔軟にオブジェクトを生成することを目的とするものです。FactoryMethod パターンでは、インスタンスの生成をサブクラスに行わせることで、より柔軟に生成するインスタンスを選択することが可能となります。
オブジェクトを生成する場合、下記のように記述するのが普通です。
Product product = new Product();
しかし、このようなオブジェクト生成方法では、十分に満足のいく結果が得られないことがあります。 そこで、FactoryMethodパターンでは、オブジェクトの生成を担うメソッド(factory method)を通して間接的にオブジェクトを生成します。
public Product factoryMethod(){ return new Product(); }
このようなオブジェクト生成方法は、直接new Product()としてオブジェクトを生成する場合に比べ、より柔軟な結果をもたらします。 それでは、サンプルケースを見てみましょう。
4.2 サンプルケース
サンプルケースでは、前回の TemplateMethod パターンに引き続き、生徒たちに版画クラスを作成させることを目的としましょう。前回と違うのは、「親クラスでは版材を与えないようにする」という点です。TemplateMethod パターンの親クラスである WoodCutPrint クラスでは、以下のようにして版材を与えていました。
private Cuttable hanzai = new Wood();
しかし、いつも人とは違ったことをしたがる今川君が「先生、僕は木じゃなくて芋に彫りたい」と言ってきたのです。生徒の自由な発想を大切にしたいと常日頃考えているあなたは、この申し出を受け入れたいと思い、親クラスを変更することにしました。
あなた : | Cuttable な hanzai を用意することを条件として、版材にどのようなインスタンスを利用するかは、すべて生徒に任せよう。 |
まずは、TemplateMethod パターンを使用した CutPrint クラスを見てみましょう。このクラスを利用して、hanzai の型の決定をサブクラスに任せられるようにすることが目的です。
public abstract class CutPrint{ protected abstract void draw( Cuttable hanzai ); protected abstract void cut( Cuttable hanzai ); protected abstract void print( Cuttable hanzai ); public void createCutPrint(){ Wood hanzai = ・・・ draw( hanzai ); cut( hanzai ); print( hanzai ); } }
さて、何が問題になるでしょう?
上リストで、ハイライトされている部分では、どのクラスのインスタンスが生成されるかは決定したくありません。なぜなら、生成するインスタンスの実際の型を決めてしまうと、サブクラスで自由に生成するインスタンスの型を決定することができないからです。createCutPrint をサブクラスでオーバーライドすればサブクラスで自由にインスタンスの型を決定することができますが、これでは、TemplateMethod の利点が失われてしまいます。
これを解決する方法として、FactoryMethod パターンが与えられます。FactoryMethod パターンでは、インスタンス生成のためのメソッドを用意します。そして、そのインスタンスを生成するためのメソッドを通してインスタンスの生成を行います。
具体的には、以下のようなソースコードとなります。
public abstract class CutPrint{ protected abstract void draw( Cuttable hanzai ); protected abstract void cut( Cuttable hanzai ); protected abstract void print( Cuttable hanzai ); protected Cuttable createCuttable(){ return new Wood(); } public void createCutPrint(){ Cuttable hanzai = createCuttable(); draw( hanzai ); cut( hanzai ); print( hanzai ); } }
このように、インスタンスを生成するメソッドを通して、インスタンスを生成するようにしておくことで、 今川君は、createCuttable メソッドをオーバーライドするメソッドを記述し、版材を自由に選択することができるようになります。実際にソースコードを見てみましょう。
public class ImagawasCutPrint extends CutPrint{ protected void draw(Cuttable hanzai){ System.out.println("マンガの絵を描く"); } protected void cut(Cuttable hanzai){ System.out.println("彫刻刀を利用して器用に彫る"); } protected void print(Cuttable hanzai){ System.out.println("インクとして、自分の血を使いプリントする"); } protected Cuttable createCuttable(){ return new Potato(); } }
これで生徒が自由に版材を選択できるようになりました。クラス図を見てみましょう。
今回は親クラスの factoryMethod にデフォルトの処理を記述し Wood オブジェクトを返すように記述しましたが、デフォルトの処理を記述せず、抽象メソッドとしておくことも可能ですね。
4.3 FactoryMethod パターンのまとめ
FactoryMethod パターンの典型的なクラス図は以下の様になります。
[引用] 『Java言語で学ぶ デザインパターン入門』(結城浩 ソフトバンクパブリッシング株式会社出版 2001年)