Javaコンストラクタと静的ファクトリメソッド
1概要
Javaコンストラクタは、完全に初期化されたクラスインスタンスを取得するためのデフォルトのメカニズムです。結局のところ、それらは依存関係を注入するために必要なすべてのインフラストラクチャを、手動または自動で提供します。
それでも、いくつかの特定のユースケースでは、同じ結果を得るために静的なファクトリメソッドに頼ることが好ましいです。
このチュートリアルでは、静的ファクトリメソッドを使用することと、従来のJavaコンストラクタを使用することの長所と短所を強調します。
2.
の利点
コンストラクタに対する静的ファクトリメソッド**
Javaのようなオブジェクト指向言語では、コンストラクタの何が問題になる可能性がありますか?全体的に、何もありません。それでも、有名なhttps://www.pearson.com/us/higher-education/program/Bloch-Effective-Java-3rd-Edition/PGM1763855.html[Joshua Block’s Effective Java Item 1]は、明確に述べています。
「コンストラクタの代わりに静的ファクトリメソッドを検討する」
__
これは大したことではありませんが、このアプローチを支持する最も説得力のある理由は次のとおりです。
-
コンストラクタは意味のある名前を持っていない
ので、彼らはいつも
言語によって課される標準の命名規則に制限されます。
-
静的ファクトリメソッドは意味のある名前を持つことができます
、したがって明示的に
彼らがしていることを伝える
。
静的ファクトリメソッドは、を実装するのと同じ型を返すことができます。
メソッド、サブタイプ、そしてプリミティブ
もあります。
柔軟な範囲の戻りタイプ
。
静的ファクトリメソッドは、以下に必要なすべてのロジックをカプセル化できます。
完全に初期化されたインスタンス
を事前に構築します。
この追加のロジックをコンストラクタから除外します。これは防ぐ
からのコンストラクタ
performing
追加のタスク、単にフィールドを初期化する以外のもの
。
静的ファクトリメソッドは、制御インスタンス化メソッド** にすることができます。
Singletonパターン
がこの機能の最も明白な例です
3 JDK
の静的ファクトリメソッド
JDKには、上記で概説した利点の多くを示す静的ファクトリメソッドの例がたくさんあります。それらのいくつかを探検しましょう。
3.1.
String
クラス
String
interning
のため、https://docs.oracle.com/javase/8/docs/を使用することはほとんどありません。 api/java/lang/String.html[
String
]新しい
String
オブジェクトを作成するためのクラスコンストラクタ。それでも、これは完全に合法です。
String value = new String("Baeldung");
この場合、コンストラクタは新しい
String
オブジェクトを作成します。これは予想される動作です。
あるいは、静的ファクトリメソッドを使って新しい
Stringオブジェクトを作成したい場合は、以下の
valueOf()メソッドの実装を使用できます。
String value1 = String.valueOf(1);
String value2 = String.valueOf(1.0L);
String value3 = String.valueOf(true);
String value4 = String.valueOf('a');
valueOf()
には、オーバーロードされた実装がいくつかあります。メソッドに渡された引数の型(
int
、
long
、
boolean
、
char、
など)に応じて、それぞれが新しい
String
オブジェクトを返します。
名前はメソッドが何をするのかをはっきりと表現しています。また、静的ファクトリメソッドの命名に関して、Javaエコシステムで確立された標準に準拠しています。
3.2.
Optional
クラス
JDKの静的ファクトリメソッドのもう1つの良い例はhttps://docs.oracle.com/javase/8/docs/api/java/util/Optional.html[
Optional
]クラスです。このクラスは、
empty()
を含む、かなり意味のある名前** のファクトリメソッドをいくつか実装しています。 、
of()
、および
ofNullable()
:
Optional<String> value1 = Optional.empty();
Optional<String> value2 = Optional.of("Baeldung");
Optional<String> value3 = Optional.ofNullable(null);
3.3.
Collections
クラス
JDKの静的ファクトリメソッドの最も代表的な例は、https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html[
Collections
]クラスです。静的メソッドのみを実装するインスタンス化不可能なクラス。
これらの多くは、提供されたコレクションに何らかの種類のアルゴリズムを適用した後にコレクションを返すファクトリメソッドです。
これがクラスのファクトリメソッドの典型的な例です。
Collection syncedCollection = Collections.synchronizedCollection(originalCollection);
Set syncedSet = Collections.synchronizedSet(new HashSet());
List<Integer> unmodifiableList = Collections.unmodifiableList(originalList);
Map<String, Integer> unmodifiableMap = Collections.unmodifiableMap(originalMap);
JDKに含まれる静的ファクトリメソッドの数は非常に多いため、例のリストは簡潔にするために短くします。
それにもかかわらず、上記の例は、ユビキタスな静的ファクトリメソッドがJavaにどのようにあるのかという明確な考えを私たちに与えるはずです。
4カスタム静的ファクトリメソッド
もちろん、** 私たちは自分自身の静的ファクトリメソッドを実装することができます。
簡単な例を見てみましょう。
この単純な
User
クラスを考えてみましょう。
public class User {
private final String name;
private final String email;
private final String country;
public User(String name, String email, String country) {
this.name = name;
this.email = email;
this.country = country;
}
//standard getters/toString
}
この場合、静的ファクトリメソッドが標準のコンストラクタより優れている可能性があることを示す目に見える警告はありません。
すべての
User
インスタンスが
country
フィールドのデフォルト値を取得したい場合
-
フィールドをデフォルト値で初期化する場合は、コンストラクタもリファクタリングする必要があるため、デザインがより硬くなります。
代わりに静的ファクトリメソッドを使用できます。
public static User createWithDefaultCountry(String name, String email) {
return new User(name, email, "Argentina");
}
country
フィールドにデフォルト値が割り当てられた
User
インスタンスを取得する方法は次のとおりです。
User user = User.createWithDefaultCountry("John", "[email protected]");
5コンストラクタからロジックを移動する
コンストラクタにさらにロジックを追加する必要がある機能を実装することを決定した場合、
User
クラスはすぐに欠陥のある設計になる可能性があります(この時点ではアラームベルが鳴っているはずです)。
すべての
User
オブジェクトが作成された時刻を記録する機能をクラスに提供したいとしましょう。
-
このロジックをコンストラクタに追加するだけでは、https://en.wikipedia.org/wiki/Single
responsibility
principle[Single Responsibility Principle]** が破られることになります。フィールドを初期化する以上のことを行うモノリシックコンストラクタになります。 -
静的なファクトリメソッドでデザインをきれいに保つことができます。**
public class User {
private static final Logger LOGGER = Logger.getLogger(User.class.getName());
private final String name;
private final String email;
private final String country;
//standard constructors/getters
public static User createWithLoggedInstantiationTime(
String name, String email, String country) {
setLoggerProperties();
LOGGER.log(Level.INFO, "Creating User instance at : {0}", LocalTime.now());
return new User(name, email, country);
}
private static void setLoggerProperties() {
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.INFO);
handler.setFormatter(new SimpleFormatter());
LOGGER.addHandler(handler);
}
}
改善された
User
インスタンスを作成する方法は次のとおりです。
User user
= User.createWithLoggedInstantiationTime("John", "[email protected]", "Argentina");
6. インスタンス制御のインスタンス化
上に示したように、完全に初期化された
User
オブジェクトを返す前に、大量のロジックを静的ファクトリメソッドにカプセル化できます。そして、関係のない複数のタスクを実行する責任を持つコンストラクタを汚染することなくこれを実行できます。
例えば、** 私たちの
User
クラスをシングルトンにしたいとしましょう。これを実現するには、インスタンス制御の静的ファクトリメソッドを実装します。
public class User {
private static volatile User instance = null;
//other fields/standard constructors/getters
public static User getSingletonInstance(String name, String email, String country) {
if (instance == null) {
synchronized (User.class) {
if (instance == null) {
instance = new User(name, email, country);
}
}
}
return instance;
}
}
getSingletonInstance()
メソッドの実装は
同期型ブロックであるため、パフォーマンスが若干低下しますが
スレッドセーフです。
この場合、インスタンス制御の静的ファクトリメソッドの実装を示すために遅延初期化を使用しました。
ただし、シングルトンを実装するための最善の方法は、Javaの
enum
型を使用することです。これは、シリアル化とスレッドの両方に対応しているためです。さまざまな方法でシングルトンを実装する方法の詳細については、https://www.baeldung.com/java-singleton[この記事]を参照してください。
予想どおり、このメソッドで
User
オブジェクトを取得すると、前の例と非常によく似ています。
User user = User.getSingletonInstance("John", "[email protected]", "Argentina");
7. 結論
この記事では、静的ファクトリー・メソッドが単純なJavaコンストラクターを使用するよりも優れた代替方法となり得る、いくつかのユースケースを検討しました。
さらに、このリファクタリングパターンは典型的なワークフローと密接に関係しているので、ほとんどのIDEがそれを実現します。
もちろん、https://netbeans.apache.org/[Apache NetBeans]、https://www.jetbrains.com/idea/[IntelliJ IDEA]、およびhttps://www.eclipse.org/downloads/?[Eclipse]は少し異なる方法でリファクタリングを実行するので、最初にあなたのIDEドキュメントをチェックするようにしてください。
他の多くのリファクタリングパターンと同様に、静的なファクトリメソッドを慎重に使用する必要があります。ただし、より柔軟でクリーンなデザインを作成することと追加のメソッドを実装する必要があることの間のトレードオフの価値があります。
いつものように、この記事で示されているすべてのコードサンプルはhttps://github.com/eugenp/tutorials/tree/master/patterns/design-patterns[GitHubで利用可能]です。