web-dev-qa-db-ja.com

依存性注入とシングルトン設計パターン

依存性注入またはシングルトンパターンをいつ使用するかをどのように識別しますか。多くのWebサイトで、「シングルトンパターンに対する依存性注入を使用する」という記事を読みました。しかし、それらに完全に同意するかどうかはわかりません。私の小規模または中規模のプロジェクトでは、シングルトンパターンの使用は簡単です。

たとえば、ロガー。 Logger.GetInstance().Log(...)を使用できますが、これの代わりに、作成したすべてのクラスをロガーのインスタンスでインジェクトする必要があるのはなぜですか?.

73
SysAdmin

テストに記録される内容を検証する場合は、依存性注入が必要です。さらに、ロガーはめったにシングルトンではありません。通常、クラスごとにロガーがあります。

このプレゼンテーションを見る テスト容易性のためのオブジェクト指向設計について、シングルトンが悪い理由がわかります。

シングルトンの問題は、特にテストでは予測が困難なグローバル状態を表すことです。

オブジェクトは事実上のシングルトンである場合がありますが、Singleton.getInstance()ではなく、依存関係注入を介して取得できることに注意してください。

Misko Heveryのプレゼンテーションで作成された重要なポイントをリストアップしています。それを見た後、オブジェクトに依存関係を定義するwhatを持たせる方がよい理由について完全な見通しを得ることができますが、ウェイを定義することはできません作成方法.

54
Bozho

シングルトンは共産主義のようなものです。どちらも紙上では素晴らしい響きをしますが、実際には問題で爆発します。

シングルトンパターンは、オブジェクトへのアクセスのしやすさに不均衡に重点を置いています。すべてのコンシューマがAppDomainスコープのオブジェクトを使用することを要求することにより、コンテキストを完全に回避し、さまざまな実装のオプションを残しません。インフラストラクチャの知識をクラスに組み込み(GetInstance()への呼び出し)、正確にzero表現力を追加します。 allを変更しない限り、1つのクラスで使用される実装を変更することはできないため、実際には表現力が低下します。 1回限りの機能を追加することはできません。

また、クラスFooLogger.GetInstance()に依存している場合、Fooはその依存関係を消費者から事実上隠しています。これは、ソースを読み、Fooに依存しているという事実を明らかにしない限り、Loggerを完全に理解したり、自信を持って使用したりできないことを意味します。ソースがない場合、依存するコードを理解し、効果的に使用する方法が制限されます。

静的プロパティ/メソッドを使用して実装されるシングルトンパターンは、インフラストラクチャの実装に関するハックにすぎません。それは無数の方法であなたを制限しますが、他の選択肢に比べて認識できる利益を提供しません。好きなように使用できますが、より優れた設計を促進する実行可能な代替手段があるため、推奨される方法ではありません。

83
Bryan Watts

他の人は、一般にシングルトンの問題を非常によく説明しています。ロガーの特定のケースに関するメモを追加したいだけです。静的なgetInstance()またはgetRootLogger()メソッドを介して、シングルトンとしてロガー(正確にはルートロガー)にアクセスすることは通常問題ではないことに同意します。 (テストしているクラスによって何がログに記録されるかを確認したい場合を除きますが、私の経験では、これが必要なケースはほとんど思い出せません。また、他の人にとっては、これはより差し迫った懸念かもしれません)。

IMOは通常、シングルトンロガーであり、テストしているクラスに関連する状態が含まれていないため、心配する必要はありません。つまり、ロガーの状態(およびその可能な変更)は、テスト対象のクラスの状態にまったく影響しません。そのため、ユニットテストが難しくなることはありません。

別の方法としては、コンストラクターを介して(ほとんど)アプリのすべてのクラスにロガーを注入します。インターフェースの一貫性を保つために、問題のクラスが現在何もログに記録していない場合でも注入する必要があります-代わりの方法は、ある時点でnowこのクラスから何かを記録する必要があり、ロガーが必要です。したがって、DIのコンストラクターパラメーターを追加して、すべてのクライアントコードを壊す必要があります。私はこれらのオプションの両方が嫌いであり、ロギングにDIを使用することは、具体的な利益なしに理論的なルールに従うために私の人生を複雑にするだけだと感じています。

つまり、私の要点は次のとおりです。(ほぼ)普遍的に使用されるが、アプリに関連する状態を含まないクラスは、シングルトンとして安全に実装できます

17
Péter Török

それはほとんどですが、テストに関するものではありません。シングルトンは消費しやすいため人気がありましたが、シングルトンには多くの欠点があります。

  • テストが難しい。つまり、ロガーが正しいことを確認する方法を意味します。
  • テストが難しい。ロガーを使用するコードをテストしているが、テストの焦点では​​ない場合、つまり、テスト環境がロガーをサポートしていることを確認する必要があります
  • 時々、シングルトンが欲しくないが、より柔軟に

DIを使用すると、依存クラスを簡単に使用できます-コンストラクターの引数に入れるだけで、システムが提供します-テストと構築の柔軟性が得られます。

9
Scott Weinstein

依存性注入の代わりにシングルトンを使用する必要があるのは、シングルトンがList.Emptyなどの不変値を表す場合のみです(不変リストを想定)。

シングルトンのガットチェックは、「これがシングルトンではなくグローバル変数であった場合でも大丈夫でしょうか?」そうでない場合は、Singletonパターンを使用してグローバル変数を検証するため、別のアプローチを検討する必要があります。

2
kyoryu

Monostateの記事をチェックアウトしてください。これはSingletonに代わる気の利いた代替手段ですが、いくつかの奇妙な特性があります。

class Mono{
    public static $db;
    public function setDb($db){
       self::$db = $db;
    }

}

class Mapper extends Mono{
    //mapping procedure
    return $Entity;

    public function save($Entity);//requires database connection to be set
}

class Entity{
public function save(){
    $Mapper = new Mapper();
    $Mapper->save($this);//has same static reference to database class     
}

$Mapper = new Mapper();
$Mapper->setDb($db);

$User = $Mapper->find(1);
$User->save();

マッパーはsave()を実行するために実際にデータベース接続に依存しているため、この種の恐ろしいことではありませんが、別のマッパーが以前に作成されている場合、依存関係を取得するこのステップをスキップできます。きちんとしている間、それはまた厄介ではないですか?

1
sunwukung
0