IOCコンテナを渡す代わりに

私はいくつかの依存関係を持つ次の基本クラスを持っていました:

public abstract class ViewModel
{
    private readonly ILoggingService loggingService;

    public ViewModel(
        ILoggingService loggingService,
        ...)
    {
        this.loggingService = loggingService;
        ...
    }
}

派生クラスでは、この基本クラスのコンストラクタですべてのパラメータを繰り返す必要はないので、これを行いました:

public abstract class ViewModel
{
    private readonly IUnityContainer container;
    private ILoggingService loggingService;
    ...

    public ViewModel(IUnityContainer container)
    {
        this.container = container;
    }

    public ILoggingService LoggingService
    {
        get
        {
            if (this.loggingService == null)
            {
                this.loggingService = this.container.Resolve();
            }

            return this.loggingService;
        }
    }

    ...
}

今私の派生クラスは、私の基本クラスのコンストラクタに一つのことを渡す必要があります。私の依存関係は、必要なときにのみ解決されるという素晴らしい効果もあります。

しかし、私はその後、IOCコンテナを渡すことは悪い考えであることを知っています。渡された多くのサービスがIOCコンテナにシングルトンとして登録されていることに留意して、最良の代替デザインパターンは何ですか?

2
"私の派生クラスでは、この基本クラスのコンストラクタですべてのパラメータを繰り返す必要はありません" - プロパティインジェクションを使用するか、コンストラクタを自動生成し、それを使って完了します。 (とにかく親クラスから子クラスを実際に意味のある方法で切り離すことはできません。)
追加された 著者 millimoose,

9 答え

あなたが述べているように、あなたはコンテナを周囲に渡すことを避けるべきです。これは、それをあなたの依存関係が何であるかをもはや見ることができず、バッグに入っているものを容易に見ることができない、「保持のバッグ」に変わります。

代わりに、あなたのコンストラクタがあまりにも多くのパラメータを取ることが分かった場合、これはそれ自身の匂いです。この場合、あなたのクラスは多すぎることをしようとしていることがよくあります(それは単一責任の原則に違反しています)。

パラメータリストを見て、パラメータを小さなグループにグループ化できるかどうかを確認してください。たとえば、コンストラクタが IEmailSender IEventLog ILoggingService を使用する場合、本当に必要なのは INotificationService です。これら3つの依存関係を集約します。

もちろん、多くの依存関係を持つコンストラクタを行う場合があります。この場合、クラスはたぶんこれらのものをまとめてワイヤリングするために使用されます。この場合、クラスは実際には何もしないでください。

7
追加された

コンストラクタ内のすべての依存関係を渡すことが最もクリーンな方法です。

派生クラスでパラメータを渡す際に問題が発生することはありません。入力を避けることは間違った動機であり、これらのコンストラクタを生成するためのResharperのようなツールがあります。

依存関係が多い場合は、そのクラスがSingle Responsibility Patternに違反していることを示します。

多くの場合、継承よりも構成を好むと良いことも考えられます。これはクラスをSRPに違反しない小さな部分に分割するのにも役立ちます。

5
追加された
あなたに同意しない!あなたは5,6以上のパラメータを渡さなければならないと思う(私の意見では4が多すぎる) - あなたのコードは読みにくくなる。そして、あなたが知っている、おそらくあなたは将来的にもっと多くのパラメータを追加したいと思います...もっと良い方法は、これらのパラメータを別々のクラスにグループ化することです。
追加された 著者 Geka P,

あなたは最初のパターンに固執するべきです。これらのコンストラクタ変数を追加するのに疲れている場合は、あまりにも多くの変数があります。あなたのクラスをより小さなビットに分割することを考えてください。このパターンは非常に強力です。なぜなら、それは怠惰によって自己調整しているからです:)

どこでも使用したいグローバル型の依存関係がある場合(ロギングは完璧な例です)...シングルトンパターンを使用してください(シングルトンパターンを使用してコンテナも作成されていることに注意してください)。

public static LoggingService
{
    private static ILoggingService _current;

    public static ILoggingService Current
    {
        get 
        {
            if(_current == null) { _current = Container.Current.Resolve(); }
            return _current;  
        }
    }
}

そのように使用してください...

LoggingService.Current.Log(...);

あなたはそれをすべてに注入する必要はありません。

あなたは一般的に、このパターンがたくさんのモジュールで使われていない限り避けるべきです...

2
追加された
-1シングルトンは反パターンと見なされます。静的メンバーを使用すると、IoCコンテナを渡すよりも悪化します。
追加された 著者 bikeshedder,

多くの継承レベルがある場合、これは面倒ですが、(コンストラクタを使用して)クラスが持つ依存関係を明示的に伝えることは良いことです。代わりに、プロパティ(セッター)インジェクションのオブジェクトに注釈を付けることが推奨されますが、これはオプションの依存関係、つまりロガーに対してのみ使用します。

1
追加された

コンテナを使用して派生クラスを作成し、必要な場所に注入します。

Wrong example - Foo worries about the dependencies of Bar as it needs to instanciate Bar.

class Foo {
    SomeDependency x;
    public Bar(SomeDependency x) {
        this.x = x;
    }
    public doSomething() {
        Bar bar = new Bar(x);
        bar.doSomething();
    }

}

class Bar {
    SomeDependency x;
    public Bar(SomeDependency x) {
        this.x = x;
    }
    public void doSomething() {
       //...
    }
}

Correct example - Foo does not care how Bar is created. Bar gets the dependencies directly from the container.

class Foo {
    SomeDependency x;
    Bar bar;

    public Bar(SomeDependency x, Bar bar) {
        this.x = x;
        this.bar = bar;
    }
    public doSomething() {
        bar.doSomething();
    }

}

class Bar {
    SomeDependency x;
    public Bar(SomeDependency x) {
        this.x = x;
    }
    public void doSomething() {
       //...
    }
}
1
追加された
bar.doSomething(); に依存関係 AnotherDependency が必要な場合はどうなりますか?したがって、 bar.doSomething(AnotherDependency methodDependency); 。あなたはこのケースをどのように扱いますか?
追加された 著者 tonix,
私は、私が意味するものを示す小さな例を加えました。 クリーンコードトーク - 見た目は見ないでください!このコンセプトは非常にうまい。
追加された 著者 bikeshedder,
@Felix継承を追加しても答えは変わらず、答えを理解することが難しくなります。質問者がIoCとDIの基本的な考え方に従わなかったという印象があります。コンテナを使ってオブジェクトを作成して配線します。
追加された 著者 bikeshedder,
しかし、この例では、BarはFooから派生したものではありません...そのため、質問には関係ありません。
追加された 著者 Felix,
私の質問は、あなたが Foo(Dep1 dep)を持っていて、派生クラスが Bar(Dep1 dep、Dep2 dep2):base(dep)派生クラスがたくさんある場合は、常に Dep1 dep を不要と書いていると思います。もちろん、dep-injの場合は、とにかく多くの派生クラスを持つべきではないでしょう
追加された 著者 Felix,

コンテナの周りを避けてください。これはサービスの場所です。 ViewModelを作成することによって、依存するロギングサービスが提供されるようにコントロールを反転する必要があります。

マーク・セーマンは彼の本に装飾を施してこれを非常にうまくやっています。誰かがすでに強調しているので、AOPは整頓された選択肢です。

コードは次のようになります。

public ViewModel(ILoggingService logger)
{
    loggingService= logger;
}

public ILoggingService LoggingService
{
    get
    {
        return this.loggingService;
    }
}
1
追加された

AOP could be a solution if you want consistent behavior across a series of classes. Here's an example on logging with PostSharp : http://www.sharpcrafters.com/solutions/logging

アドホックロギングの場合、これはあなたのニーズに完全には合っていないかもしれません。

1
追加された

私は、コンストラクタに追加されたパラメータの数を減らすために、以下のインターフェイスとクラスを作成して、工場パターンを使用しました。

public interface IInfrastructureFactory
{
    ILoggingService LoggingService { get; }
   //... Other Common Services Omitted ...
}

public class InfrastructureFactory : IInfrastructureFactory
{
    private readonly ILoggingService loggingService;
   //... Other Common Services Omitted ...

    public InfrastructureFactory(
        ILoggingService loggingService,
       //... Other Common Services Omitted ...
        )
    {
        this.loggingService = loggingService;
       //... Other Common Services Omitted ...
    }

    public ILoggingService LoggingService
    {
        get { return this.loggingService; }
    }

   //... Other Common Services Omitted ...
}

私のIOCコンテナでは、IInfrastructureFactoryを一度登録します。私のビューモデルでは、依存関係は1つしかなく、新しいビューモデルを作成する方がはるかに簡単で簡単です。

public abstract class ViewModel
{
    private readonly IInfrastructureFactory infrastructureFactory;

    public ViewModel(IInfrastructureFactory infrastructureFactory)
    {
        this.infrastructureFactory = infrastructureFactory;
    }

    public ILoggingService LoggingService
    {
        get { return this.infrastructureFactory.LoggingService; }
    }

   //... Other Common Services Omitted ...
}
0
追加された