web-dev-qa-db-ja.com

「シングルトン」工場、いいか悪いか?

私は(抽象的な)工場をたくさん持っていますが、それらは通常、シングルトンとして実装されています。

通常は、これらのファクトリを使用したり知ったりすることにまったくビジネスを持たないレイヤーを通過させる必要がないという便宜のためです。

ほとんどの場合、起動時にコードプログラムの残りの部分であるファクトリ実装を決定するだけでよく、おそらくいくつかの構成を通じて

それは例えば見えますお気に入り

abstract class ColumnCalculationFactory { 
  private static ColumnCalculationFactory factory;

 public static void SetFactory(ColumnCalculationFactory f) {
         factory = f;
  }

  public static void Factory() {
         return factory;
  }

 public IPercentCalculation CreatePercentCalculation();
 public IAverageCalculation CreateAverageCalculation();
    ....

}

これについて何かにおいがしますが、私は何を言っているのかわかりません-それはシングルトンというよりもむしろ混乱したグローバルなのかもしれません。それは本当にそこにあるようなものではありませんする必要があります ColumnCalculationsを作成する唯一のファクトリーです-私のプログラムはこれ以上必要としません。

これはベストプラクティスと見なされますか?これらをいくつかの(半)グローバルAppContextクラスに詰め込む必要がありますか?何か他のもの(私はいくつかのより大きなIoCコンテナ、またはspring.netにまだ切り替える準備ができていません)?

31
leeeroy

それは本当にあなたがやっていることとあなたのアプリケーションの範囲に依存します。それがかなり小さいアプリであり、これを超えて成長することがない場合は、現在のアプローチで十分です。これらのことに対する普遍的な「ベスト」プラクティスはありません。ステートレスリーフメソッドや一方向の呼び出し(ロギングなど)以外の目的でシングルトンを使用することはお勧めしませんが、シングルトンであるという理由だけでそれを手作業で却下することは必ずしも適切なことではありません。

些細なコードやプロトタイプのコード以外については、すべての依存関係が考慮され、「驚き」が得られないことを意味するため、私は個人的に、コンストラクターインジェクションで制御の反転を明示的に使用することを好みます。コンパイラでは、BなしでAとCなしでBをインスタンス化できません。シングルトンはこれらの関係をすぐに埋めます。AからBなしでAとBなしでインスタンス化できます。AからBへの呼び出しが発生すると、null参照が取得されます。例外。

実行時エラーを介して逆方向に繰り返し作業する必要があるため、これはテスト時に特に厄介です。コードをテストするときは、他のコーダーと同じようにAPIを使用しているため、このアプローチの設計上の問題を示しています。コンストラクターの注入により、これが発生することは決してありません-すべての依存関係は前もって述べられます。コンストラクターインジェクションの欠点は、オブジェクトグラフの構成がより複雑になることです。これは、IoCコンテナを使用することで軽減されます。

私が言おうとしているのは、ある種のコンテキストオブジェクトとレジストリパターンの使用を検討している段階に達している場合は、IoCコンテナも検討していると思います。独自のmuttバージョンをローリングする努力をすることは、Autofacのような確立された無料の製品を使用できる場合、おそらく時間の無駄です。

14
Mark Simpson

少数のシングルトンを持つことはかなり典型的であり、通常問題にはなりません-多くのシングルトンはいくつかの刺激的なコードにつながります。

シングルトンを大量に含むクラスをテストしなければならない状況を経験しました。問題は、クラスbをテストしていて、クラスc(シングルトン)を取得する場合、クラスcをモックアウトする方法がないことです(少なくともEasyMockでは、シングルトンクラスの静的ファクトリメソッドを置き換えることができません。

簡単な修正の1つは、テスト用にすべてのシングルトンに「セッター」を用意することです。あまりお勧めしません。

私たちが試みたもう1つのことは、すべてのシングルトンを保持する単一のクラス、つまりレジストリを持つことでした。これを行うと、ほぼ確実に使用する必要がある依存性注入にかなり近づきます。

テスト以外に、私はずっと前に、特定のオブジェクトの複数のインスタンスが存在することは決してないということを学びました。次のリビジョンでは、2つが必要になることがよくあります。これにより、シングルトンは静的クラスよりもはるかに優れています。少なくとも、シングルトンのゲッターにパラメーターを追加し、あまりリファクタリングせずに2つ目のパラメーターを返すことができます(これも、DIが行うことを行うだけです) )。

とにかく、DIを見てください。あなたは本当に幸せかもしれません。

11
Bill K

いいえ、ここで行っているのはグローバルな状態を作成することです。グローバルな状態にはさまざまな種類の問題があります。そのうちの1つは、他の関数の動作にかなり目に見えない形で依存していることです。関数が終了する前にfactoryの保存と復元を忘れている別の関数を呼び出すと、どこかに保存しない限り古い値を取り戻すことができないため、問題が発生します。そして、それを行うにはコードを追加する必要があります(コードから判断すると、finallyの言語を使用していると思います。これにより、間違いの余地がさらに広がります)。さらに、2つのサブオブジェクトに対して2つのファクトリをすばやく切り替える必要があるコードが発生した場合は、各ポイントでメソッド呼び出しを記述する必要があります-各サブオブジェクトに状態を格納することはできません(そうすることはできますが、あなたは(確かに多くはない)グローバルな国家の目的を打ち負かす)。

おそらくファクトリタイプをメンバーとして格納し、それを必要とする新しいオブジェクトのコンストラクターに渡す(または必要に応じて新しいものを作成するなど)のが最も理にかなっています。また、コントロールが向上します。オブジェクトAによって構築されたすべてのオブジェクトが同じファクトリを通過することを保証したり、ファクトリを交換するメソッドを提供したりできます。

6
coppro

これはベストプラクティスと見なされますか?

問題ありません。「私のプログラムはこれ以上必要ありません」と言ったので、より柔軟で抽象的なものを実装することは You Ai n't Gonna Need It の場合かもしれません。

4
ChrisW

これらのファクトリーを呼び出すコードのためのまともな単体テストを書くのが非常に難しくなるので、私はそれに対して助言します。

Misko Heveryのブログに 素敵な記事 に関する記事があります。

4
jqno

シングルトンの悪い形は、次のようにFactoryメソッドと同等のものを実装するものです。

return new CalculationFactory ();

これは、スタブ、モック、ラップするのがはるかに困難です。

他の場所で作成されたオブジェクトを返すバージョンははるかに優れていますが、ほとんどの場合と同様に、誤用または乱用される可能性があります。

1
soru