クラスの束縛から解放して、疎結合なアプリケーションを作る。ファクトリーメソッドパターンについて

デザインパターン

なぜこの記事を書いたのか

Java 言語で学ぶデザインパターン入門の第2弾の記事です。

前回はテンプレートメソッドパターンについて記事を書きましたが、今回はその応用編でファクトリーメソッドパターンを学んでいきます。

このデザインパターンから学べることは、実際のアプリケーションの開発現場でも活かしやすい実践的な考え方を含んでいます。

それでは早速見ていきましょう。

ファクトリーメソッドパターンについて

ファクトリーメソッドパターンではインスタンス生成の処理の流れだけをフレームワークとして定め、具体的なインスタンスの生成処理をサブクラス側で実装していくといったデザインパターンです。

これは、以前にも記事で紹介したテンプレートメソッドパターンのひとつの実用例とも言えます。

テンプレートメソッドパターンは、抽象メソッドの組み合わせからなるテンプレートメソッドで処理の流れだけが決まり、具体的な処理は抽象メソッドを実装するサブクラス側で定義されていきます。

こうすることで、共通化されつつも拡張性の高いアプリケーションを設計するためのデザインパターンでした。

ファクトリーメソッドパターンはこれをインスタンス生成の処理に利用したものというわけです。

それでは実際にサンプルコードを見ていきましょう。

サンプルコードを見てみる

サンプルコードでは、とある団体で使用される ID カードを作成するアプリケーションを考えます。

ID カードを作成する、という行為には以下の2つの処理が含まれます。

  • ID カードを作成する
  • ID カードを持っている所有者リストに、作成したカードの所有者の名前を追加する

また、作成される ID カードは使用できます。

ただし、今回この部分は重要でないので、簡単のため ID カードを使用する = 「〇〇のカードを使います」という文字列を出力すること、とします。

idcard.use //=> 〇〇のカードを使います

use メソッドを使うと、以上のように出力されるということですね。

以上を踏まえて、今回は以下の流れでアプリケーションを設計していきます。

  1. use メソッドのインタフェースを持つ、 Product クラスを作成する
  2. Product クラスを継承して、IDCard クラスを作成する
  3. Product クラスのインスタンス作成の処理の流れだけを定めた、Factory クラスを作成する
  4. 具体的に IDCard クラスのインスタンスを作る処理を実装した、IDCardFactory クラスを作成する

今回作成されるクラスの関係をクラス図に表すと以下のようになります。

1. use メソッドのインタフェースを持つ、 Product クラスを作成する

Product クラスは、製品を表すフレームワークになります。

Product クラスは use メソッドを持つ、というインタフェースのみを提供します。

2. Product クラスを継承して、IDCard クラスを作成する

IDCard クラスは実際に作成される製品のクラスです。

このアプリケーションでは製品は、use() をインタフェースに持つと約束されています。
なので、こちらは Product を継承します。

3. Product クラスのインスタンス作成の処理の流れだけを定めた、Factory クラスを作成する

Factory クラスは、Product のインスタンスを生成するためのファクトリー(工場)のクラスです。

このクラスでは create メソッドという名前で、インスタンス生成の処理を実行します。

create メソッドでは、「製品を作る」(createProduct)=>、「所有者リストに登録する」(registerProduct)を行う処理の流れだけが定義されています。

しかし、createProductregisterProduct は共に抽象メソッドになっているので、具体的な処理の内容はこの段階ではわかりません。

要するに create メソッドは、テンプレートメソッド になっています。

4. 具体的に IDCard クラスのインスタンスを作る処理を実装した、IDCardFactory クラスを作成する

最後に Factory クラスで抽象メソッドとなっていた、 createProduct, registerProduct を実装した、 IDCardFactory を作成していきます。

createProduct は、 IDCard のインスタンスを作成して、これを返却します。

registerProduct は、 IDCard のインスタンスがもつ、 owner を自らの owners のリストに追加します。

ここで初めて、テンプレートメソッドとして作成した Factory#create の処理が確定します。

実際にクライアントとして Main からこの IDCard 作成処理を実行してみましょう。

インスタンス生成にクラス名を指定した、new を使用することはありません。

Factory 型の factory を生成し、 create を実行することでインスタンスを得ます。

このときに、Factory として振る舞えるサブクラス、IDCardFactory を使用しています。

実際にどんなインスタンスが得られるのかは、 IDCardFactory の実装に依るところとなります。

ファクトリーメソッドパターンのメリット

具体的なクラス名の束縛から解放される

ファクトリーメソッドパターンでは、インスタンス生成の呼び出しを new メソッドではなく、 Factory#create に置き換えます。

こうすることで、IDCardFactory 以外のプログラムに具体的なクラス名が登場しません。このことは、アプリケーションがクラス名の束縛から解放されたことを意味します。

クラス名の束縛から解放することにどのようなメリットがあるのでしょうか。

それはクラスの変更の影響を受けないということです。

例えば IDCard というクラス名を IdCard に変更したいとき、どこを修正すべきでしょうか。

ファクトリーメソッドパターンを使っていれば、 IDCardFactorycreateProduct, registerProduct を修正するだけで済みます。

至る所で IDCard を直接 new していると、その全てを変更する必要があります。

このように他のクラスやパッケージ間の結びつきを減らし、依存関係が局所化された状態のことを疎結合といいます。

できる限り疎結合な状態にしておくことは、アプリケーションの保守性を高めるために極めて有効なアプローチとなります。

ロジックを共通化できる

テンプレートメソッドパターンと同様で、インスタンス生成のロジックを共通化できることも大きなメリットです。

新しく社員に貸与する PC という製品を追加したいとします。

このときも IDCard のときと同様に作れば OK です。

Factory クラスを継承した、PCFactory を作成し、createProduct, registerProduct で PC のインスタンスを生成、登録する処理を実装します。

PC を追加するための開発は IDCard 作成機能を作った本人が実装するとは限りませんが、既存の処理を参考にすればおそらく同じように実装してくれるはずです。

このように、設計者の意図が常に伝わるようにしておくことで、アプリケーションの一貫性は高まります。

おわりに

今回は、ファクトリーメソッドパターンについて見ていきました。

具体的な実装方法の紹介をした上で、アプリケーション設計において重要な考え方である、疎結合にも触れました。

デザインパターンを学ぶことで、抽象的な概念をより具体的な方法で理解できることができます。

今後もこの本で学んだ内容をわかりやすく紹介していければと考えていますので、ぜひ読んでいただけると幸いです。

コメント

タイトルとURLをコピーしました