Joshua Bloch の「Effective Java Programming Language Guide」を読んでいました。
彼は、スタティック ファクトリ メソッドを使用して不要な重複オブジェクトを回避できると説明しています。
私はこれをよく理解していません。
誰でも説明できますか?
7 に答える
実際の例:
Java は、バイトを表すプリミティブ型とオブジェクト型の両方をサポートしています。プリミティブをオブジェクトに変換すると、次のようなことができます。
Byte b = new Byte( (byte) 65);
しかし、これは呼び出しごとに新しいインスタンスを作成します。代わりに、次のようにします。
Byte b = Byte.valueOf( (byte) 65);
すべての呼び出しで、メソッド valueOf() は、バイト値 65 を表す Byte オブジェクトの同じインスタンスを返します。
10000 回の呼び出しの後、1 番目の例では 10000 個のオブジェクトが作成されますが、2 番目の例では 1 つしか作成されません。Byte クラスには、-128 から 127 までのすべての数値を表す Byte オブジェクトの内部キャッシュがあるためです。
非複製に関するすべての回答は、非複製の良い例であるシングルトンパターンに焦点を当てているようですが、一般的なケースで使用するには悪いパターンです。私の見解では、特定のアプリケーションには0から1のシングルトンが含まれている必要があり、ゼロが優先されます。ただし、それは不要なオブジェクトを作成しないこととはほとんど関係がありません。
代わりに、多くのDateオブジェクトを作成する必要があるアプリケーションを検討してください。非常に多くのDateオブジェクトを作成しているため、Dateオブジェクトの作成がパフォーマンスに悪影響を及ぼしています。したがって、Dateオブジェクトのコンストラクターを呼び出す代わりに、コードはファクトリメソッドを介してのみDatesを作成するようにリファクタリングされます。このファクトリメソッド内では、マップがチェックされ、要求された日付がすでに作成されているかどうかが確認されます。そうであった場合は、同じオブジェクトがマップから返されます。それ以外の場合は、新しいものが作成され、マップに配置されて返されます。
混乱しているように思われるのは、ファクトリメソッドを呼び出すことで重複オブジェクトの作成を防ぐ方法です。ファクトリメソッドを呼び出すだけでは、実際には何も変わりません。ファクトリを呼び出すことで許可されるのは、コードがオブジェクトの作成を引き継いで決定することです。新規に電話する場合、そのような決定を下すことはできません。
パターンとその用途についての洞察については、この質問も参照してください。
コンストラクターを呼び出すと、常に新しいオブジェクトが返されます (例外がスローされない限り)。静的ファクトリ メソッド、またはあらゆる種類のファクトリは、常に新しいオブジェクトを返す必要はありません。たとえばgetInstance()
、従来の Singleton デザイン パターンのメソッドは、常にまったく同じオブジェクトを返すファクトリ メソッドです。オブジェクトを 1 回だけインスタンス化できるようにするか、何らかの種類のオブジェクト プールを作成するかなど、この種のことを実行したい場合があります。静的ファクトリ メソッド。主な目的は、適切な名前の擬似コンストラクターを作成することです。
これは、静的ファクトリ メソッドを使用して適切な名前の擬似コンストラクタを作成する (ややばかげた) 例です。このクラスを考えてみましょう:
class Person {
public Person(Role role) {
setRole(role);
}
...
}
静的ファクトリ メソッドがなければ、次のようにすることができます。
Person employee = new Person(Role.EMPLOYEE);
Person manager = new Person(Role.MANAGER);
代わりに、静的ファクトリ メソッドを作成できます。
class Person {
public static Person newEmployee() {
return new Person(Role.EMPLOYEE);
}
public static Person newManager() {
return new Person(Role.MANAGER);
}
private Person(Role role) {
setRole(role);
}
...
}
代わりに次のようにすることもできます。
Person employee = Person.newEmployee();
Person manager = Person.newManager();
これは良い例ではないかもしれませんが、より複雑なコンストラクターまたは説明の少ないパラメーターを持つコンストラクターを考えてみてください。ファクトリ メソッド ルートを使用すると、コードがより明確になる場合があります。もちろんデメリットもありますが…
オブジェクトの作成を制限する限り、CEO は 1 人しか存在できないなど、いくつかの奇妙な制約を考慮してください。
class Person {
private static Person singletonCEO = new Person(Role.CEO);
public static Person newCEO() {
return singletonCEO;
}
...
}
そしてそれがどのように作成されるか:
Person ceo1 = Person.newCEO();
Person ceo2 = Person.newCEO();
assertThat(ceo1, is(ceo2)); // JUnit 4.x
これらの例がお役に立てば幸いです。
私の記憶がよければ、彼も本の中で例を挙げています。を考慮してくださいDecimal
。ゼロはかなり頻繁に使用されます。したがって、静的ファクトリ メソッドを呼び出すとDecimal.valueOf("0")
(これが実際の API かどうかはわかりませんが、この例では問題になりません)、0 を表す Decimal のインスタンスが返されます。どの呼び出しでも同じインスタンス。実装は次のようになります。
public class Decimal {
private static Decimal zero = new Decimal(0);
public static Decimal valueOf(String s) {
if (s.equals("0")) {
return zero;
} else {
return new Decimal(parse(s)); // or whatever
}
// rest of the class
}
ゼロのインスタンスは 1 つしかないことに注意してください。その他の数値については、新しいオブジェクトが作成されます。また、これはファクトリ メソッドで機能し、コンストラクターではこれを行うことはできません。それが、ブロッホが前者の利点として指摘しようとしていたことです。
そして、Yishai が述べたように、Singleton とはそれほど密接に関連していません。ご覧のとおり、多数の Decimal オブジェクトを使用できます。代わりに、ファクトリ メソッドを使用して、作成するインスタンスの数を完全に制御できます。そのため、工場と呼ばれています。
ファクトリ メソッド パターンは、アクションを実行するためにオブジェクトの新しいインスタンスを作成する必要がない場合に役立ちます。
以下は、同じオブジェクトを返す静的ファクトリ メソッドが役立つと考えられる一般的なケースの 2 つです。
オブジェクトの作成にはコストがかかります-- オブジェクトがインスタンス化されると多くの処理が発生するため、オブジェクトを複数回インスタンス化することは望ましくありません。(これはSingleton パターンにも関連しています。)
オブジェクトは状態を保持しません-- インスタンス間に状態の違いがない場合、毎回新しいオブジェクトを作成する目的はありません。
ファクトリ メソッド パターンに関するウィキペディアのページには、このトピックに関する詳細情報があります。
具体例を見てみましょう。
このDateFormat
クラスはgetInstance
静的メソッドを使用してDateFormat
インスタンスを返します。これを使用してDate
、マシンのロケールに応じて事前設定された書式にフォーマットできます。
返される はすべての日付フォーマット アクションに同じフォーマットを使用しているため、毎回新しいインスタンスDateFormat
を作成する本当の理由はありません。DateFormat
一般に、これが実装される方法は、インスタンスがまだ存在しない場合にインスタンスを作成し、そのインスタンスへの参照を保持することです。インスタンスが再度必要になると、参照が返されます。(これは通常、Singleton パターンの実装方法でもあります。)
例えば:
class MySingleInstanceObject {
private MySingleInstanceObject instance;
private MySingleInstanceObject() {
// Initialize the object.
// This may be expensive.
}
public MySingleInstanceObject getInstance() {
if (instance == null) {
instance = new MySingleInstanceObject();
}
return instance;
}
}
(参考までに、上記のコードはシングルトンの例です。また、スレッドセーフではありません。)
私はここでこの本のいくつかを読むことができました。彼が書いたものを読んだ後、彼が言っているのは、静的ファクトリメソッドは開発者としての柔軟性を高め、返されるものをより明確にすることもできるということのようです。これをコンストラクターと対比すると、コンストラクターは何が返されるかに関して明確さを提供しない場合があります。さらに、私が魅力的だと思った静的ファクトリメソッドでキャッシュなどを行うことができます。このレベルの制御と柔軟性が必要な場合、このアプローチは良いアプローチのように思われます。
キャッシングを使用する場合は、不要な重複オブジェクトを作成しないことが重要です。この静的ファクトリアプローチを使用すると、静的ファクトリメソッドを呼び出すたびに同じオブジェクトを返すことができます。
例:
public class Person
{
private Person(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
}
public string FirstName {get; private set;}
public string LastName {get; private set;}
private static Dictionary<string, Person> objectPool = new Dictionary<string, Person>();
private object lockObject = new object();
public static Person CreatePerson(string firstName, string lastName)
{
var result = objectPool[firstName + lastName];
Person person = null;
if (result != null)
{
return result
}
lock(lockObject)
{
person = new Person(firstName, lastName);
objectPool.Add(firstName + lastName, person)
}
return person;
}
}
オブジェクトのインスタンス化を作成するファクトリ クラスがある場合、オブジェクトを作成するたびに、ファクトリ クラスもインスタンス化する必要があります。基本的に、このファクトリ クラスの複製を作成することになります。
静的である場合、使用されるファクトリのインスタンスは 1 つだけです。