web-dev-qa-db-ja.com

静的ファクトリメソッドとインスタンス(通常)コンストラクター?

両方が利用可能な言語で、インスタンスコンストラクターまたはインスタンスを返す静的メソッドを見たいですか?

たとえば、_char[]_からStringを作成する場合:

  1. String.FromCharacters(chars);

  2. new String(chars);

46
Dan Goldstein

Effective Java、第2版 では、JoshuaBlochは確かに前者を推奨しています。覚えている理由はいくつかありますが、覚えていない理由は間違いありません。

  • メソッドに意味のある名前を付けることができます。インスタンスを構築する方法が2つあり、どちらもintを取りますが、そのintの意味が異なる場合は、通常のメソッドを使用すると、呼び出し元のコードがはるかに読みやすくなります。
  • 最初の結果-同じパラメータリストで異なるファクトリメソッドを持つことができます
  • 「潜在的に予想される失敗」の場合はnullを返すことができますが、コンストラクターは常に値を返すか、例外をスローします
  • 宣言されたもの以外の型を返すことができます(たとえば、派生クラスを返す)
  • これをファクトリとして使用して、同じオブジェクトへの参照を複数回返す可能性があります

欠点:

  • 現在、それは慣用的なものではありません-開発者は「新しい」を見ることに慣れています
  • 「新しい」と表示された場合know新しいインスタンスを取得しています(モジュロ 最近言及した奇数
  • サブクラスで適切なコンストラクターを使用できるようにする必要があります
  • C#3では、コンストラクター呼び出しは、オブジェクト初期化式を使用してコンパクトな方法でフィールド/プロパティを設定できます。この機能は静的メソッド呼び出しには適用されません
55
Jon Skeet

インスタンスの作成に副作用がない場合、つまりコンストラクターが実行しているのがプロパティの初期化のみである場合に、コンストラクターを作成します。インスタンスの作成が、通常コンストラクターに期待されないことを行う場合は、静的メソッドを作成します(そしてコンストラクターをプライベートにします)。

例えば:

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の作成と異なる理由を教えてくれませんが、私が見る必要があることを教えてくれます。

22
Robert Rossney

私は最近パブリックAPIに取り組んでおり、静的ファクトリメソッドとコンストラクターのどちらを選択するかについて悩んでいます。静的ファクトリメソッドが確かに理にかなっている場合もありますが、それがそれほど明確ではなく、APIの他の部分との一貫性がコンストラクターにそれらを含めるのに十分な理由であるかどうかはわかりません。

とにかく、私は Josh BlochとのBill-Vennersインタビューからの引用 に出くわしました。

クラスを作成しているときに、パブリックコンストラクターに対する静的ファクトリの利点に関する私の本のリストを実行できます。これらの利点のかなりの数が実際にあなたのケースに当てはまることがわかった場合は、静的ファクトリを使用する必要があります。それ以外の場合は、コンストラクターを使用する必要があります。

私の本でそのアドバイスを見つけてがっかりした人もいました。彼らはそれを読んで、「あなたは公共の静的工場について非常に強く主張しているので、デフォルトでそれらを使用するべきです」と述べました。そうすることの唯一の本当の欠点は、コンストラクターを使用してオブジェクトを作成することに慣れている人々にとっては少し当惑することだと思います。そして、私はそれがプログラムの視覚的な手がかりを少し少なく提供すると思います。 (新しいキーワードは表示されません。)また、Javadocはすべてのコンストラクターをグループ化するため、ドキュメントで静的ファクトリを見つけるのは少し難しくなります。しかし、私は誰もが常に静的な工場を考慮し、適切なときにそれらを使用する必要があると言います。

その引用を読んだ後、 riが言及した研究 *、他にやむを得ない理由がない限り、コンストラクターを支持することに誤りを犯す傾向があります。正当な理由のない静的ファクトリメソッドは、おそらく不必要な複雑さと過剰なエンジニアリングであると思います。明日までにまた気が変わってしまうかもしれませんが….

*残念ながら、この調査では静的ファクトリメソッドではなくファクトリパターン(新しいインスタンスを作成するために別のファクトリオブジェクトが存在する)に焦点を当てているため、静的ファクトリメソッドが多くのプログラマを混乱させるという結論を実際に引き出すことができるかどうかはわかりません。研究は私に彼らがしばしばそうするという印象を与えましたが。

10
MB.

オブジェクトが不変である場合は、静的メソッドを使用してキャッシュされたオブジェクトを返し、メモリの割り当てと処理を節約できる場合があります。

9
Dan Goldstein

コンストラクタとファクトリパターンのユーザビリティを研究したICSE'07の論文があります。私はファクトリパターンを好みますが、この調査では、開発者が正しいファクトリメソッドを見つけるのに時間がかかることが示されました。

http://www.cs.cmu.edu/~NatProg/papers/Ellis2007FactoryUsability.pdf

7
Uri

静的メソッド。次に、例外をスローするのではなく、nullを返すことができます(参照型を除く)

2
Darren Kopp

インスタンスコンストラクターが好きなのは、それが私にとってより理にかなっており、表現しようとしているものとの潜在的なあいまいさが少ないためです(つまり、FromCharactersが単一の文字を受け取るメソッドである場合)。確かに主観的ですが。

2
Nick

コンストラクターを使用して構築する必要があるため、個人的には通常のコンストラクターを使用することを好みます。ただし、notを使用する正当な理由がある場合、つまりFromCharactersが新しいメモリを割り当てないと明示的に述べている場合は、価値があります。呼び出しの「新しい」には意味があります。

2
nimish

場合によります。インスタンスコンストラクターの使用が「通常」である言語の場合、正当な理由がない限り、通常はインスタンスコンストラクターを使用します。これは驚き最小の原則に従います。

ちなみに、あなたは別の一般的なケースを忘れていました:初期化メソッドとペアになったnull /デフォルトコンストラクタ。

2
ejgottl

JonSkeetがJoshBlochを言い換えたように、多くの場合、静的ファクトリメソッドがコンストラクタよりも好ましい理由はいくつかあります。クラスが高価なセットアップや複雑な使用法のない単純なクラスである場合は、慣用的なコンストラクターを使用してください。最新のJVMは、オブジェクトの作成を非常に高速かつ安価にします。クラスがサブクラス化されている可能性がある場合、またはクラスを不変にすることができる場合(並行プログラミングにとって大きな利点であり、ますます重要になるだけです)、ファクトリメソッドを使用します。

もう1つのヒント。ファクトリメソッドに名前を付けないでくださいFoo.new*またはFoo.create*。これらの名前のメソッドは常に新しいインスタンスを返す必要があり、そうするとファクトリメソッドの大きな利点の1つが失われます。より適切な命名規則はFoo.of*またはFoo.for*Google Guavaライブラリ (以前のGoogleコレクションライブラリ)は、これをうまく処理します。

1
Dov Wasserman

もちろん、コンストラクターに対する静的ファクトリメソッドにはいくつかの利点があります。

  1. ファクトリメソッドは、柔軟なコードを書くのに役立ちます。一方、コンストラクターはそれを緊密に結合しました。
  2. 静的ファクトリメソッドを使用すると、さまざまなインスタンスを作成できるため、読みやすさが向上します。
  3. キャッシュされたオブジェクトを返すことができます。たとえば、シングルトンのgetInstance()
0
Sumanth Varada