Effective Java(第3版)の読書会で出た疑問点について補足します。
(JavaDocへのリンクは日本語訳が読みやすいため、意図的にJava8のものにしています)
5つ目の長所(返されるオブジェクトの実装クラスはファクトリメソッドが書かれた時点で存在しなくても良い)ついて
JDBCのDBコネクション生成を例として記載していますが、JDBCの知識が無いと理解が難しいです。
JDBCは、標準ライブラリではDB操作のためのインターフェースであるConnectionと、それを取得するためのファクトリメソッドであるDriverManager.getConnectionが定義されています。
DriverManagerクラスはJavaの標準ライブラリとして定義されていますが、
DB操作をするためのConnectionの実装は標準ライブラリには入っていません。
Connectionの実装はDB製品に依存するため、Oracle、MySQL、postgressql等のDB制作元が、
決められた作法に従って実装してくれています。
DBを使うには、DB製品のJDBCのjarファイルを入手し、CLASSPATHを通したうえで、
決められた引数でファクトリメソッドを呼べはConnectionインスタンスが入手できる、
という仕組みです。
このファクトリメソッドの使用方法について、
ファクトリメソッド定義時点で実装クラスが存在しなくても良いというメリットを解説しています。
残念ながらこのようなファクトリメソッドのメリットは
DIの使用が前提の開発では生かされることはないはずですが、
DriverManagerの使用方法は、抽象(インタフェース)に対してプログラミングを行い、
実装に依存しないという、現在のDIにつながる抽象化の初期実装を垣間見ることができます。
ファクトリメソッドの使い所
シングルトン以外でファクトリメソッドを自分で定義するケースが思いつかない
という意見がありました。
例として、
期間を表現するPeriodクラスを考えてみます。
Periodクラスは基本的に開始日時と終了日時を持ちますが、
期間としては開始と終了の両方が決まっているケースだけでなく
- 開始日時だけが決まっていて、それ以降はすべて期間内
- 終了日時だけが決まっていて、それ以前はすべて期間内
の様な期間が考えられます。
これらについて、コンストラクタで定義すると、片方の日時をnullで表現することになりますが
それよりも以下のようにファクトリメソッドで定義したほうが、可読性が高くなります。
// ファクトリメソッドなら名前で意図が伝わる
public static Period startingFrom(LocalDateTime start) { ... }
public static Period endingAt(LocalDateTime end) { ... }
public static Period between(LocalDateTime start, LocalDateTime end) { ... }
private Period(LocalDateTime start, LocalDateTime end) { ... }
他にも、不変な値クラスで直前に生成したインスタンスと同じ値を生成することが多い場合、
ファクトリメソッドを用いて直近で生成したインスタンスを保存しておき、
キャッシュ処理を入れる等の使い方が思いつきます。