第4章 Factory Method - 増補改訂版Java言語で学ぶデザインパターン入門

第4章は、Factory Methodパターン。というわけで、復習メモ。

Factory Methodの特徴

Factory Methodパターンでは、インスタンスを直接newせず、実装クラスに生成させます。

インスタンス生成の枠組は、抽象クラスで定義します。どのクラスのインスタンスを生成するかという部分は、具象クラスに任されます。

Factory Methodを使う利点

Productのインスタンスを生成するとき、コンストラクタではなくfactoryメソッドを使うと、何がうれしいのか? その答えは、本のp.52に書かれています。

具体的なクラス名による束縛からスーパークラスを解放している

Productの実装クラスが増えても、インスタンス生成用のスーパークラス(抽象クラスのCreatorとProduct)を変更しなくて済みます。

本文のサンプルコードでは、クライアントコードでFactoryのインスタンスを作っています。しかし、staticなfactoryメソッドを使えば、Factoryのインスタンス生成は不要になります(どちらかというと、staticにしたコードをよく見る)。もちろん、この章の問題4-2のように、Factoryクラスの具象クラスに情報を持たせたいときは、Factoryのインスタンスを使う必要があります。

パッケージ外から直接newさせないために

問題4-1の解説にあるとおり、パッケージ外から直接newさせたくないときは、Productの具象クラスのコンストラクタをパッケージプライベート(デフォルト)にします。こうすることで、Factoryクラスの具象クラス経由でインスタンス生成することを強制できます。

インスタンス生成の3種類の実装方法

Factoryクラスに記述するインスタンス生成の実装方法が、3種類紹介されています。

  • 抽象メソッドにする
    • サンプルコードと同じです。具象クラスで実装します。
  • デフォルトの実装を用意する
    • 具象クラスで実装されなければ、デフォルトの実装が使われます。
  • エラーにする
    • 具象クラスで実装されなければ、エラーになります。

Factory Methodの本質は?

このように、Factory Methodパターンは、応用例をいろいろ見かけます。まとまった例を見つけたので引用すると、次のようなものです。

  • Factory クラスが単独 (サブクラス化なし) で存在し、ファクトリメソッドがスタティックメソッドではないもの
  • 抽象ファクトリクラスに getFactory() のようなメソッドを保持し、その中で具象ファクトリクラスを生成し、抽象ファクトリ型で返却するようなもの
  • ファクトリクラスを抽出せず、ファクトリメソッドの追加のみ行うもの
事例で学ぶデザインパターン 第 4 回 | オブジェクトの広場

Factory Methodの本質を押さえたコードであれば、形が違ってもFactory Methodパターンであることを意識して使う必要があります。
「パターン利用と開発者の意思疎通」という項が本文にもあり、設計者の意図を汲んでコードを拡張するための工夫の話が書かれています。

では、Factory Methodの本質とは何だろう? 何を満たせば、Factory Methodといえるのか。つまり、〜Factoryという名前をクラスにつけてよいのか? これまで、「クライアントコードでインスタンスを直接newせず、どのクラスのインスタンスを作成するかは実装コードに任せる(スーパークラスは何も知らない)」コードをすべてFactory Methodだと思いながらコードを読んでいたけど、これでいいのかなぁ。

factoryメソッドの命名

本文にはありませんが、factoryメソッドのイディオムとしてよく見かける命名規則をメモ。

  • インスタンス取得するfactoryメソッド:getInstance
    • (例)java.util.CalendarのgetInstance(TimeZone zone)
    • このメソッドの中でcreateCalendar(TimeZone zone,Locale aLocale)が呼ばれ、その中でGregorianCalendarクラスのインスタンスがnewされる。
  • 特に、型変換の役割をするfactoryメソッド:valueOf
    • (例)java.lang.IntegerのvalueOf(String s)
    • Stringを受け取って意味的に同じIntegerを返す。StringをIntegerに型変換する。

これって、get〜、set〜がJavaBeansの命名規則であるように、どこかに大元があるんだっけ?