見出し画像

Effective Java(3rd) Item 1.2 Consider static factory methods instead of constructors



7P

Java 8より前は、インターフェイスは静的メソッドを持てなかった。慣例により、単数形の(named type)インターフェイスに対するstatic factory methodsは複数形の(named types)noninstantiable companion class(Item 4)に含まれていた。例えば、Java Collections Frameworkunmodifiable collectionssynchronized collectionsなどを提供しながら、そのインターフェイスの、45utility implementationsを持っている。これらのimplementationのほぼ全ては、あるnoninstantiableなクラス(java.util.Collections)におけるstatic factory methodsにより書き出される。返されるオブジェクトのクラスは全てpublicではない。

コレクションフレームワークAPIははるかに小さい。それは減らされたAPIのbulkだけでなく、conceptual weightでもある。つまり、APIを使うためにプログラマーがマスターすべき概念の数の多さや難しさである。プログラマーは返されたオブジェクトがインターフェイスで特定されるAPIをまさに持っていることを知っているので、追加でimplementation classのクラスドキュメントを読む必要はない。さらに、そのようなstatic factory methodを使う際にimplementation classよりむしろインターフェイスによって返されたオブジェクトを参照することを求められる。これは一般的にいい練習である(Item 64)。

Java 8の時、インターフェイスは静的メソッドを持てないという制限はなくなったので、インターフェイスにnoninstantiable companion classを提供する理由はほとんどない。そのようなクラスに慣れた、多くのpublicな静的メンバは代わりにインターフェイス自体に置かれるべきである。しかし、別々のパッケージプライベートなクラスにおける、これらの静的メソッドの背後にあるimplementation codeの内部に依然として置く必要があることに注意しよう。Java 8がインターフェイスの全ての静的メンバがpublicであることを要請したからである。Java 9はprivateな静的メソッドを認めたが、静的フィールドと静的メンバクラスは依然としてpublicであることを要請される。

4つ目の利点は返されたオブジェクトのクラスが入力された引数の関数から異なることができることである。宣言された戻り値の型のいかなるsubtypeも許される。返されるオブジェクトのクラスもまたリリースごとに変われる。

EnumSetクラス(Item36)はpublicpなコンストラクタを持っておらず、static factoriesだけである。OpenJDKでは、それらは基礎となるenum typeのサイズに依存して、二つのサブクラスのうちの一つを返す。つまり、64個以下の要素数を持っているなら、たいていのenum typesがそうだが、static factoriesは単一のlong型に支えられた、RegularEnumSetインスタンスを返す。そして、64個以上の要素数を持つenum typeなら、factoriesはlong型配列に支えられた、JumboEnumSetインスタンスを返す。

これら二つの実装クラスの存在はクライアントには見えない。もし、RegularEnumSetが小さいenum typesにパフォーマンス面で優位であることをやめたら、悪影響なく将来のリリースから削除されるだろう。同様に、パフォーマンスが良かったら、3番目、4番目のEnumSetの実装として将来のリリースに加えられるだろう。

8P
 
クライアントはfactoryから返った、オブジェクトのクラスを知りもしないし気を遣ったりもしない。彼らはEnumSetのあるクラスにしか気を遣わない。

5番目の利点は、メソッドを含むクラスが書かれた際に、返されたオブジェクトのクラスは存在する必要はないということである。そのような柔軟なstatic factory methodsはJDBCのような、service provider frameworksの基礎を作る。service provider frameworksは、プロバイダがサービスを実装するシステムで、そのシステムにより、クライアントを実装から切り離して、実装可能になる。


この記事が気に入ったらサポートをしてみませんか?