両方が利用可能な言語では、インスタンス コンストラクターとインスタンスを返す静的メソッドのどちらが好きですか?
たとえば、 aString
から aを作成している場合char[]
:
String.FromCharacters(chars);
new String(chars);
両方が利用可能な言語では、インスタンス コンストラクターとインスタンスを返す静的メソッドのどちらが好きですか?
たとえば、 aString
から aを作成している場合char[]
:
String.FromCharacters(chars);
new String(chars);
『Effective Java, 2nd edition』で、Joshua Bloch は確かに前者を推奨しています。覚えている理由はいくつかありますが、覚えていない理由もいくつかあります。
欠点:
インスタンスの作成に副作用がない場合、つまり、コンストラクターが行うのはプロパティの初期化だけである場合、コンストラクターを記述します。インスタンスを作成するときに、通常コンストラクターが行うとは思わないことを行う場合は、静的メソッドを作成します (そしてコンストラクターをプライベートにします)。
例えば:
public class Foo
{
private Foo() { }
private static List<Foo> FooList = new List<Foo>();
public static Foo CreateFoo()
{
Foo f = new Foo();
FooList.Add(f);
return f;
}
}
私はこの慣習を守っているので、
Foo f = Foo.CreateFoo();
Bar b = new Bar();
コードを読んでいると、これら 2 つの行のそれぞれが何をしているのかについて、非常に異なる一連の期待を持っています。そのコードは、Foo の作成が Bar の作成と何が違うのかを教えてはくれませんが、調べる必要があることを教えてくれます。
私は最近パブリックAPIに取り組んでおり、静的ファクトリメソッドとコンストラクターのどちらを選択するかについて悩んでいます。静的ファクトリメソッドは確かに意味がある場合もありますが、それほど明確ではない場合もあり、APIの他の部分との一貫性が、コンストラクターにそれらを含めるのに十分な理由であるかどうかはわかりません。
とにかく、私はジョシュ・ブロックとのビル・ベナーズのインタビューからの引用に出くわしました。
クラスを作成しているときに、パブリックコンストラクターに対する静的ファクトリの利点に関する私の本のリストを実行できます。これらの利点のかなりの数が実際にあなたのケースに当てはまることがわかった場合は、静的ファクトリを使用する必要があります。それ以外の場合は、コンストラクターを使用する必要があります。
私の本でそのアドバイスを見つけてがっかりした人もいました。彼らはそれを読んで、「あなたは公共の静的工場について非常に強く主張しているので、デフォルトでそれらを使用するべきです」と述べました。そうすることの唯一の本当の欠点は、コンストラクターを使用してオブジェクトを作成することに慣れている人々にとっては少し戸惑うことだと思います。そして、私はそれがプログラムの視覚的な手がかりを少し少なく提供すると思います。(新しいキーワードは表示されません。)また、Javadocはすべてのコンストラクターをグループ化するため、ドキュメントで静的ファクトリを見つけるのは少し難しくなります。しかし、私は誰もが常に静的な工場を考慮し、適切なときにそれらを使用する必要があると言います。
その引用とUriが言及した研究*を読んだので、他にやむを得ない理由がない限り、コンストラクターを支持することに誤りを犯す傾向があります。正当な理由のない静的ファクトリメソッドは、おそらく不必要な複雑さと過剰なエンジニアリングであると思います。明日までにまた気が変わってしまうかもしれませんが…
*残念ながら、この調査では静的ファクトリメソッドではなく、ファクトリパターン(新しいインスタンスを作成するために別のファクトリオブジェクトが存在する)に焦点を当てているため、静的ファクトリメソッドが多くのプログラマを混乱させるという結論を実際に引き出すことができるかどうかはわかりません。研究は私に彼らがしばしばそうするであろうという印象を与えましたが。
オブジェクトが不変の場合、静的メソッドを使用してキャッシュされたオブジェクトを返し、メモリの割り当てと処理を節約できる場合があります。
コンストラクタとファクトリ パターンの使いやすさを研究した ICSE'07 の論文があります。私はファクトリ パターンを好みますが、調査によると、開発者は正しいファクトリ メソッドを見つけるのが遅いことがわかりました。
http://www.cs.cmu.edu/~NatProg/papers/Ellis2007FactoryUsability.pdf
場合によります。インスタンス コンストラクターの使用が「通常」である言語の場合、使用しない正当な理由がない限り、通常はインスタンス コンストラクターを使用します。これは最小の驚きの原則に従います。
ところで、もう 1 つの一般的なケースを忘れていました: 初期化メソッドとペアになった null/default コンストラクターです。
静的メソッド。次に、例外をスローするのではなく、null を返すことができます (参照型を除く)。
私はインスタンス コンストラクターを好みます。これは、それが私にとってより理にかなっており、表現しようとしているものとの潜在的なあいまいさが少ないためです (つまり、FromCharacters が単一の文字を取るメソッドである場合)。もちろん主観ですが。
コンストラクターは構築に使用する必要があるため、個人的には通常のコンストラクターを見ることを好みます。ただし、使用しない十分な理由がある場合、つまり FromCharacters が新しいメモリを割り当てないことを明示的に示している場合は、使用する価値があります。呼び出しの「新しい」には意味があります。
JonSkeetがJoshBlochと言い換えたように、多くの場合、静的ファクトリメソッドがコンストラクタよりも好ましい理由はいくつかあります。クラスが高価なセットアップや複雑な使用法のない単純なクラスである場合は、慣用的なコンストラクターを使用してください。最新のJVMは、オブジェクトの作成を非常に高速かつ安価にします。クラスがサブクラス化されている可能性がある場合、またはクラスを不変にすることができる場合(並行プログラミングにとって大きな利点であり、これはますます重要になります)、ファクトリメソッドを使用します。
もう1つのヒント。Foo.new*
ファクトリメソッドまたはに名前を付けないでくださいFoo.create*
。これらの名前のメソッドは常に新しいインスタンスを返す必要があり、そうするとファクトリメソッドの大きな利点の1つが失われます。より適切な命名規則はFoo.of*
またはFoo.for*
です。Google Guavaライブラリ(以前のGoogleコレクションライブラリ)は、これをうまく処理しています。