web-dev-qa-db-ja.com

シングルトンが悪い場合、なぜサービスコンテナーが良いのですか?

私たちは皆、悪いシングルトンが依存関係を隠しているため、そして 他の理由 であるということを知っています。

しかし、フレームワークでは、一度だけインスタンス化して、どこからでも(ロガー、dbなど)を呼び出す必要がある多くのオブジェクトが存在する可能性があります。

この問題を解決するために、サービス(ロガーなど)へのすべての参照を内部的に保存するいわゆる「オブジェクトマネージャー」(または Service Container symfonyのような)を使用するように言われました。

しかし、なぜサービスプロバイダーは純粋なシングルトンほど悪くないのでしょうか?

サービスプロバイダーは依存関係も隠し、最初のインスタンスの作成をラップアウトします。だから、シングルトンの代わりにサービスプロバイダーを使用する理由を理解するのに本当に苦労しています。

PS。依存関係を隠さないために、DIを使用する必要があることを知っています(Miskoによると)

追加

私は付け加えます:最近、シングルトンはそれほど悪いものではありません、PHPUnitの作成者はここでそれを説明しました:

DI +シングルトンは問題を解決します:

<?php
class Client {

    public function doSomething(Singleton $singleton = NULL){

        if ($singleton === NULL) {
            $singleton = Singleton::getInstance();
        }

        // ...
    }
}
?>

これがすべての問題をまったく解決しなくても、それはかなり賢い方法です。

DIとサービスコンテナ以外に、このヘルパーオブジェクトにアクセスするための適切なソリューションはありますか?

88
dynamic

Service Locatorは、言うまでもなく、2つの悪の少ない方です。これらの4つの違いに「より少ない」煮詰めます(少なくとも今のところ、他のことは考えられません):

単一責任の原則

サービスコンテナは、シングルトンのように単一責任原則に違反しません。シングルトンはオブジェクトの作成とビジネスロジックを組み合わせ、サービスコンテナはアプリケーションのオブジェクトライフサイクルを厳密に管理します。その点で、サービスコンテナの方が優れています。

カップリング

シングルトンは通常、静的メソッド呼び出しのためにアプリケーションにハードコーディングされます。これにより、コード内で 密結合およびモック困難な依存関係 になります。一方、SLは単なる1つのクラスであり、注入することができます。したがって、クラス化されたものはすべてそれに依存しますが、少なくともそれは疎結合の依存関係です。したがって、ServiceLocatorをシングルトン自体として実装しない限り、それはいくぶん優れており、テストも容易です。

ただし、ServiceLocatorを使用するすべてのクラスは、カップリングの一種であるServiceLocatorに依存するようになりました。 ServiceLocatorのインターフェイスを使用することでこれを緩和できるため、具体的なServiceLocator実装に縛られることはありませんが、クラスは何らかのLocatorの存在に依存しますが、ServiceLocatorをまったく使用しないと再利用が劇的に増加します。

非表示の依存関係

ただし、依存関係を非表示にする問題は非常に多く存在します。ロケーターを使用するクラスにインジェクトするだけでは、依存関係はわかりません。しかし、シングルトンとは対照的に、SLは通常、舞台裏で必要なすべての依存関係をインスタンス化します。したがって、サービスを取得するとき、 CreditCardの例ではMisko Hevery のようにはなりません。依存関係のすべての依存関係を手動でインスタンス化する必要はありません。

インスタンス内部から依存関係を取得することも Law of Demeter に違反しています。これは、コラボレーターに掘り下げてはならないことを示しています。インスタンスは、直接の協力者とのみ会話する必要があります。これは、SingletonとServiceLocatorの両方の問題です。

グローバルステート

また、テスト間で新しいService Locatorをインスタンス化すると、以前に作成されたすべてのインスタンスも削除されるため(グローバルステートの問題も多少緩和されます(間違いを犯してSLの静的属性に保存しない限り)。もちろん、SLが管理するクラスのグローバル状態には当てはまりません。

さらに詳細な説明については、 Service Locator vs Dependency Injection のFowlerも参照してください。


更新に関するメモと Singletonsを使用するコードのテストに関するSebastian Bergmann によるリンクされた記事:Sebastianは、提案された回避策がSingleonsを使用することで問題が少なくなることを決して示唆しません。他の方法ではテストしにくいコードを作成する方法の1つにすぎません。しかし、まだ問題のあるコードです。実際、彼は明示的に次のように述べています。「できるからといって、すべきではない」。

75
Gordon

サービスロケーターパターンはアンチパターンです。依存関係を公開する問題は解決しません(クラスの定義を見ても、その依存関係はインジェクトされず、代わりにサービスロケーターからヤンクされているためわかりません)。

だから、あなたの質問は次のとおりです。なぜサービスロケーターは良いですか?私の答えは、そうではありません。

避けて、避けて、避けてください。

42
jason

サービスコンテナは、シングルトンパターンと同様に依存関係を隠します。代わりに、依存関係注入コンテナーを使用することをお勧めします。これは、サービスコンテナーのすべての利点を備えていますが、サービスコンテナーの欠点はありません(私の知る限り)。

私が理解する限り、2つの唯一の違いは、サービスコンテナでは、サービスコンテナが注入されるオブジェクトであるため(依存関係を隠す)、DICを使用する場合、DICは適切な依存関係を注入します。 DICによって管理されているクラスは、DICによって管理されているという事実を完全に無視しているため、結合が少なくなり、依存関係が明確になり、ユニットテストが正常に実行されます。

これはSOの両方の違いを説明する良い質問です: 依存性注入とサービスロケーターパターンの違いは何ですか?

4
rickchristie

サービスコンテナ内のオブジェクトを簡単に置き換えることができるため
1)継承(Object Managerクラスを継承でき、メソッドをオーバーライドできます)
2)設定の変更(Symfonyの場合)

また、シングルトンはカップリングが大きいためだけでなく、_Single _ tonsであるため、不良です。ほとんどすべての種類のオブジェクトにとって、これは間違ったアーキテクチャです。

「コンストラクターで」「純粋な」DIを使用すると、非常に大きな代償を払うことになります。すべてのオブジェクトは、コンストラクターに渡される前に作成する必要があります。メモリの使用量が増え、パフォーマンスが低下することを意味します。また、常にオブジェクトを作成してコンストラクターに渡すことができるわけではありません-依存関係のチェーンを作成できます...私の英語は、それについて完全に議論するには十分ではありません。

2
OZ_

私にとっては、単純な理由でグローバル定数、シングルトンを避けるようにしています。APIの実行が必要になる場合があります。

たとえば、フロントエンドと管理者がいます。管理者の内部で、ユーザーとしてログインできるようにしたいと思います。 admin内のコードを検討してください。

$frontend = new Frontend();
$frontend->auth->login($_GET['user']);
$frontend->redirect('/');

これは、フロントエンドの初期化のために新しいデータベース接続、新しいロガーなどを確立し、ユーザーが実際に存在するか、有効であるかなどを確認します。また、適切な個別のCookieおよびロケーションサービスを使用します。

シングルトンの私の考えは次のとおりです。親の中に同じオブジェクトを2回追加することはできません。例えば

$logger1=$api->add('Logger');
$logger2=$api->add('Logger');

単一のインスタンスとそれを指している両方の変数が残ります。

最後に、オブジェクト指向開発を使用する場合は、クラスではなくオブジェクトを操作します。

0
romaninsh