42

私は現在、DIとSLの長所と短所を比較検討しています。しかし、私は次のキャッチ22に気づきました。これは、すべてにSLを使用し、各クラスにIoCコンテナーのみを注入する必要があることを意味します。

DIキャッチ22:

Log4Netなどの一部の依存関係は、単にDIに適合しません。私はこれらのメタ依存関係を呼び出しており、コードの呼び出しに対して不透明である必要があると感じています。私の正当な理由は、単純なクラス「D」が元々ロギングなしで実装され、その後ロギングを必要とするようになった場合、依存クラス「A」、「B」、および「C」は、何らかの方法でこの依存関係を取得し、 「A」から「D」(「A」が「B」を構成し、「B」が「C」を構成すると仮定)。1つのクラスにログインする必要があるという理由だけで、コードを大幅に変更しました。

したがって、メタ依存関係を取得するには不透明なメカニズムが必要です。シングルトンとSLの2つが思い浮かびます。前者には、主に厳密なスコープ機能に関して既知の制限があります。せいぜいシングルトンは、アプリケーションスコープ(つまり静的変数)に格納されているAbstractFactoryを使用します。これによりある程度の柔軟性が得られますが、完全ではありません。

より良い解決策は、IoCコンテナーをそのようなクラスに注入し、そのクラス内からSLを使用して、コンテナーからのこれらのメタ依存関係を解決することです。

したがって、キャッチ22:クラスにIoCコンテナーが注入されているので、他のすべての依存関係も解決するためにそれを使用してみませんか?

私はあなたの考えを大いに感謝します:)

4

11 に答える 11

61

クラスには現在IoCコンテナが注入されているので、他のすべての依存関係も解決するためにそれを使用してみませんか?

サービスロケーターパターンを使用すると、依存性注入の主要なポイントの1つが完全に無効になります。依存性注入のポイントは、依存性を明示的にすることです。コンストラクターで明示的なパラメーターを作成しないことでこれらの依存関係を非表示にすると、本格的な依存性注入は実行されなくなります。

これらはすべて、Foo(Johnny Cashの曲のテーマに設定された)という名前のクラスのコンストラクターです。

間違い:

public Foo() {
    this.bar = new Bar();
}

間違い:

public Foo() {
    this.bar = ServiceLocator.Resolve<Bar>();
}

間違い:

public Foo(ServiceLocator locator) {
    this.bar = locator.Resolve<Bar>();
}

右:

public Foo(Bar bar) {
    this.bar = bar;
}

Bar後者のみが明示的に依存します。

ロギングに関しては、ドメインコードに浸透せずにそれを行う正しい方法があります(そうすべきではありませんが、浸透する場合は、依存性注入期間を使用します)。驚くべきことに、IoCコンテナはこの問題に役立ちます。ここから始めてください。

于 2011-02-13T17:14:18.090 に答える
7

Service Locatorはアンチパターンです。理由は、http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspxで詳しく説明されています。ロギングに関しては、それを他の依存関係と同じように扱い、コンストラクターまたはプロパティインジェクションを介して抽象化を注入することができます。

log4netとの唯一の違いは、サービスを使用する呼び出し元のタイプが必要なことです。 Ninject(または他のコンテナ)の使用サービスを要求しているタイプを見つけるにはどうすればよいですか?これを解決する方法を説明します(Ninjectを使用しますが、すべてのIoCコンテナーに適用できます)。

または、ロギングを横断的関心事と考えることもできます。これは、ビジネスロジックコードと混合するのは適切ではありません。その場合、多くのIoCコンテナによって提供されるインターセプトを使用できます。 http://msdn.microsoft.com/en-us/library/ff647107.aspxは、Unityでのインターセプトの使用について説明しています。

于 2011-02-13T17:22:28.877 に答える
5

私の意見はそれが依存するということです。ある方が良い場合もあれば、別の場合もあります。しかし、一般的に私はDIを好むと思います。その理由はいくつかあります。

  1. 依存関係が何らかの形でコンポーネントに注入されると、そのインターフェイスの一部として扱うことができます。したがって、コンポーネントのユーザーがこの依存関係を提供するのは簡単です。インジェクションされたSLまたは静的SLの場合、依存関係は非表示になり、コンポーネントの使用は少し難しくなります。

  2. 注入された依存関係は、単純にモックできるため、単体テストに適しています。SLの場合は、ロケーター+モックの依存関係を再度設定する必要があります。ですから、それはより多くの仕事です。

于 2011-02-13T17:14:27.423 に答える
4

ロギングはAOPを使用して実装できる場合があり、ビジネスロジックと混同しないようになっています。

それ以外の場合、オプションは次のとおりです。

  • オプションの依存関係(setterプロパティなど)を使用し、単体テストではロガーを挿入しません。実稼働環境で実行する場合、IOCコンテナが自動的に設定を行います。
  • アプリのほぼすべてのオブジェクトが使用している依存関係がある場合(「ロガー」オブジェクトが最も一般的な例です)、シングルトンアンチパターンが適切な方法となる数少ないケースの1つです。これらを「良いシングルトン」と呼ぶ人もいます: http//aabs.wordpress.com/2007/12/31/the-ambient-context-design-pattern-in-net/

もちろん、単体テストにスタブ/モックを使用できるように、このコンテキストは構成可能である必要があります。AmbientContextのもう1つの推奨される使用法は、現在の日付/時刻プロバイダーをそこに配置して、単体テスト中にスタブできるようにし、必要に応じて時間を短縮することです。

于 2012-04-12T11:50:59.877 に答える
2

これは、MarkSeemanによる「ServiceLocatorisaAnti-Pattern」に関するものです。私はここで間違っているかもしれません。しかし、私も自分の考えを共有すべきだと思いました。

public class OrderProcessor : IOrderProcessor
{
    public void Process(Order order)
    {
        var validator = Locator.Resolve<IOrderValidator>();
        if (validator.Validate(order))
        {
            var shipper = Locator.Resolve<IOrderShipper>();
            shipper.Ship(order);
        }
    }
}

OrderProcessorのProcess()メソッドは、実際には「制御の反転」の原則に従っていません。また、メソッドレベルでの単一責任の原則を破ります。メソッドがインスタンス化に関係する必要があるのはなぜですか

オブジェクト(新規または任意のSLクラスを介して)それは何かを達成する必要があります。

Process()メソッドでオブジェクトを作成する代わりに、コンストラクターは、以下に示すように、実際にそれぞれのオブジェクトのパラメーター(依存関係の読み取り)を持つことができます。次に、サービスロケーターをIOCとどのように変えることができるか

容器。また、ユニットテストにも役立ちます。

public class OrderProcessor : IOrderProcessor
{
    public OrderProcessor(IOrderValidator validator, IOrderShipper shipper)
    {
        this.validator = validator; 
        this.shipper = shipper;
    }

    public void Process(Order order)
    {

        if (this.validator.Validate(order))
        {
            shipper.Ship(order);
        }
    }
}


//Caller
public static void main() //this can be a unit test code too.
{
var validator = Locator.Resolve<IOrderValidator>(); // similar to a IOC container 
var shipper = Locator.Resolve<IOrderShipper>();

var orderProcessor = new OrderProcessor(validator, shipper);
orderProcessor.Process(order);

}
于 2017-06-01T09:13:10.487 に答える
1

私はJavaでGoogleGuiceDIフレームワークを使用しましたが、テストを簡単にするだけではないことを発見しました。たとえば、アプリケーションごとに(クラスではなく)個別のログが必要でしたが、さらに、すべての共通ライブラリコードが現在の呼び出しコンテキストでロガーを使用する必要がありました。ロガーを注入することでこれが可能になりました。確かに、すべてのライブラリコードを変更する必要がありました。ロガーはコンストラクターに挿入されました。最初は、必要なすべてのコーディング変更のため、このアプローチに抵抗しました。最終的に、変更には多くの利点があることに気づきました。

  • コードがシンプルになりました
  • コードははるかに堅牢になりました
  • クラスの依存関係が明らかになりました
  • 依存関係が多い場合は、クラスのリファクタリングが必要であることを明確に示しています。
  • 静的シングルトンが排除されました
  • セッションまたはコンテキストオブジェクトの必要性がなくなりました
  • DIコンテナはスレッドを1つだけ含むように構築できるため、マルチスレッドがはるかに簡単になり、不注意による相互汚染が排除されました。

言うまでもなく、私は現在DIの大ファンであり、最も些細なアプリケーションを除くすべてのアプリケーションに使用しています。

于 2013-05-08T17:35:18.687 に答える
1

妥協点にたどり着きました。DIを使用しますが、最上位の依存関係をオブジェクトにバンドルして、依存関係が変更された場合のリファクタリングを回避します。

以下の例では、派生したすべての依存関係をリファクタリングすることなく、「ServiceDependencies」に追加できます。

例:

public ServiceDependencies{
     public ILogger Logger{get; private set;}
     public ServiceDependencies(ILogger logger){
          this.Logger = logger;
     }
}

public abstract class BaseService{
     public ILogger Logger{get; private set;}

     public BaseService(ServiceDependencies dependencies){
          this.Logger = dependencies.Logger; //don't expose 'dependencies'
     }
}


public class DerivedService(ServiceDependencies dependencies,
                              ISomeOtherDependencyOnlyUsedByThisService                       additionalDependency) 
 : base(dependencies){
//set local dependencies here.
}
于 2016-03-18T13:47:11.310 に答える
1

私はこの質問が少し古いことを知っています、私はちょうど私の意見を与えると思いました。

実際には、10回のうち9回は、実際にはSLを必要とせず、DIに依存する必要があります。ただし、SLを使用する必要がある場合があります。SL(またはそのバリエーション)を使用していると思う分野の1つは、ゲーム開発です。

SLのもう1つの利点は(私の意見では)、internalクラスを回すことができることです。

以下に例を示します。

internal sealed class SomeClass : ISomeClass
{
    internal SomeClass()
    {
        // Add the service to the locator
        ServiceLocator.Instance.AddService<ISomeClass>(this);
    }

    // Maybe remove of service within finalizer or dispose method if needed.

    internal void SomeMethod()
    {
        Console.WriteLine("The user of my library doesn't know I'm doing this, let's keep it a secret");
    }
}

public sealed class SomeOtherClass
{
    private ISomeClass someClass;

    public SomeOtherClass()
    {
        // Get the service and call a method
        someClass = ServiceLocator.Instance.GetService<ISomeClass>();
        someClass.SomeMethod();
    }
}

ご覧のとおり、ライブラリのユーザーは、このメソッドが呼び出されたことを知りません。これは、DIを実行しなかったためであり、とにかく実行できるというわけではありません。

于 2018-04-14T17:21:03.630 に答える
0

例でlog4netのみを依存関係として使用する場合は、次のようにするだけで済みます。

ILog log = LogManager.GetLogger(typeof(Foo));

log4netは、型(または文字列)をパラメーターとして使用することで詳細なロギングを提供するため、依存関係を挿入する意味はありません。

また、DIはSLと相関していません。IMHO ServiceLocatorの目的は、オプションの依存関係を解決することです。

例:SLがILogインターフェイスを提供する場合、ロギングdaaを書き込みます。

于 2012-07-06T14:32:05.777 に答える
0

人々が本当にDIが唯一の良いIOCパターンであると言っていることを私は知っていますが、私はこれを理解していません。SLを少し売ってみます。新しいMVCコアフレームワークを使用して、私が何を意味するかを示します。最初のDIエンジンは本当に複雑です。人々がDIと言うときに本当に意味するのは、Unity、Ninject、Autofacなどのフレームワークを使用することです。これらのフレームワークはすべての面倒な作業を行います。SLはファクトリクラスを作成するのと同じくらい簡単です。小規模で高速なプロジェクトの場合、これは適切なDIのフレームワーク全体を学習せずにIOCを実行する簡単な方法です。習得するのはそれほど難しくないかもしれませんが、それでもなおです。ここで、DIが発生する可能性のある問題について説明します。MVCコアドキュメントからの引用を使用します。「ASP.NETCoreは、依存性注入をサポートおよび活用するためにゼロから設計されています。」ほとんどの人は、DIについて「コードベースの99%はIoCコンテナの知識がないはずだ」と言っています。[FromServices]属性。これで、DIの人々は、あなたはこのようなものではなく工場で行くことになっていると言うでしょうが、あなたが見ることができるように、MVCを作っている人々でさえそれを正しくしませんでした。DIの問題はフィルターにも表示され、フィルターでDIを取得するために何をする必要があるかを確認します。

public class SampleActionFilterAttribute : TypeFilterAttribute
{
    public SampleActionFilterAttribute():base(typeof(SampleActionFilterImpl))
    {
    }

    private class SampleActionFilterImpl : IActionFilter
    {
        private readonly ILogger _logger;
        public SampleActionFilterImpl(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation("Business action starting...");
            // perform some business logic work

        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            // perform some business logic work
            _logger.LogInformation("Business action completed.");
        }
    }
}

SLを使用した場合、var _logger = Locator.Get();を使用してこれを行うことができます。そして、ビューに行きます。DIに関する善意がすべてあるので、彼らはビューにSLを使用しなければなりませんでした。新しい構文@inject StatisticsService StatsServiceは。と同じvar StatsService = Locator.Get<StatisticsService>();です。DIの最も宣伝されている部分は、ユニットテストです。しかし、人々がやっていることは、目的のないモックサービスをテストすること、または実際のテストを行うためにDIエンジンを接続する必要があることです。そして、私はあなたが悪いことを何でもできることを知っています、しかし人々はそれが何であるかを知らなくてもSLロケーターを作ることになります。多くの人が最初にそれを読まずにDIを作るわけではありません。DIに関する私の最大の問題は、クラスのユーザーがそれを使用するために他のクラスの内部動作を認識している必要があることです。
SLは良い方法で使用でき、その単純さの中でもとりわけいくつかの利点があります。

于 2016-10-01T17:48:59.393 に答える
0

DIの場合、注入されたタイプのアセンブリを厳密に参照する必要がありますか?誰もそれについて話しているのを見ません。SLの場合、config.jsonなどから、必要に応じてタイプを動的にロードする場所をリゾルバーに指示できます。また、アセンブリに数千のタイプとその継承が含まれている場合、それらを登録するためにサービスコレクションプロバイダーへの数千のカスケード呼び出しが必要ですか?それは私が多くの話を見るところです。ほとんどの人がDIの利点とそれが一般的に何であるかについて話し、.netでそれを実装する方法に関しては、ハードリンクされた型アセンブリへの参照を追加するための拡張メソッドを提示しました。それは私にとってあまり切り離されていません。

于 2019-08-04T17:15:04.030 に答える