4

いくつかの依存関係を持つ次の基本クラスがありました。

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<IUnityContainer>();
            }

            return this.loggingService;
        }
    }

    ...
}

これで、派生クラスは基本クラス コンストラクターに 1 つのものを渡すだけで済みます。また、依存関係が必要な場合にのみ解決されるという優れた効果もあります。

しかし、その後、IOC コンテナーを渡すのは悪い考えであることがわかりました。渡されたサービスの多くがシングルトンとして IOC コンテナーに登録されていることを念頭に置いて、最良の代替設計パターンは何ですか?

4

9 に答える 9

10

あなたが述べているように、コンテナを周りに渡さないようにする必要があります。これにより、依存関係が何であるかを確認できなくなり、バッグの中に何が入っているかを簡単に確認できなくなります。

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

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

もちろん、多くの依存関係を持つコンストラクターがある場合もあります。この場合、クラスはおそらくこれらのものをまとめて接続するために使用されます。この場合、クラスはおそらく実際の作業を行わないようにする必要があります。

于 2013-01-29T11:18:53.657 に答える
7

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

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

多くの依存関係がある場合は、クラスが単一責任パターンに違反していることを示しています。

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

于 2013-01-29T12:09:38.287 に答える
1

コンテナを回さないでください。これはサービスの場所です。ViewModelを作成するものがすべて、依存するロギングサービスを提供するように、制御を反転する必要があります。

マーク・シーマンは、彼の本の中でこれを非常にうまく装飾しています。誰かがすでに強調しているように、AOPはきちんとした代替手段です。

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

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

public ILoggingService LoggingService
{
    get
    {
        return this.loggingService;
    }
}
于 2013-01-29T11:21:11.643 に答える
1

コンテナを介して派生クラスを作成し、必要な場所に注入するだけです。

間違った例-インスタンス化する必要があるためFoo、 の依存関係が心配です。BarBar

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() {
        // ...
    }
}

正しい例-がどのように作成されたFooかは気にしませんBarBarコンテナから依存関係を直接取得します。

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() {
        // ...
    }
}
于 2013-01-29T10:53:45.863 に答える
1

一連のクラス全体で一貫した動作が必要な場合は、AOPが解決策になる可能性があります。PostSharp を使用したロギングの例を次に示します: http://www.sharpcrafters.com/solutions/logging

ただし、アドホック ロギングの場合、これはニーズに完全に合わない場合があります。

于 2013-01-29T11:12:29.177 に答える
1

CommonServiceLocatorは、静的呼び出しを介してリソースを解決する方法を提供できます。

Unity docs Using Injection Attributesでは、コンストラクター インジェクションが適切でない場合に選択する他の方法を示しています。

共通の基本クラスがある場合、ロギングにプロパティ セッター インジェクションを使用するのが大好きです。

abstract class Widget
{
   [Dependency]
   public ILogger { set; set; } // Set it and forget it!
}

メソッド コール インジェクションを実際に使用したことがあるとは言えません。

すべての依存関係を常にコンストラクター経由で受け取るようにすべてを設計することはできないという理由だけで、何か間違ったことをしているように感じるべきではありません...おそらく完璧な世界では、オプションがあると便利です.. .

于 2013-01-29T11:24:57.853 に答える
1

When you have many levels of inheritance, this can be a hassle, but it's a good thing to explicitly tell what dependencies a class have (through the constructor). An alternative is Annotating Objects for Property (Setter) Injection, but I recommend that you use this only for optional dependencies, i.e. a logger.

于 2013-01-29T11:13:27.120 に答える
-2

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

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

public static LoggingService
{
    private static ILoggingService _current;

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

次に、次のように使用します...

LoggingService.Current.Log(...);

そうすれば、すべてに注入する必要はありません。

多くのモジュールで使用されない限り、通常、このパターンは避けるべきです...

于 2013-01-29T10:59:58.207 に答える