web-dev-qa-db-ja.com

DependencyInjection-コンストラクターの過剰注入の匂い対サービスロケーター-適切なアプローチはどこにありますか?

私はMVCプロジェクトを改善しようとしています。DI、IoCコンテナー、コンストラクターインジェクション、およびサービスロケーターに関するいくつかの記事を読みました。依存関係を支援するためにNinjectを使いたかったのですが、読んだもののいくつかに戸惑いました。

最も物議を醸しているのは、ここで広く議論されているものです http://jeffreypalermo.com/blog/constructor-over-injection-anti-pattern (両方の部分)。

そして、それに対するいくつかの応答-- Mark Seemannの応答フォローアップ など

ジェフリーは私の恐怖を表現し、不正確な方法でそれらに対処します-しかし、私は私の場合に何をすべきかわかりません。

たとえば、MVCコントローラーを見てみましょう。 MCVアプリとWebAPI間の通信レイヤーとして機能します。私は、コンストラクターインジェクションを実行する際に必要となる依存関係の数について、少し納得しています。

public class OcrController : ConnectorControllerBase, IOcrController
{
    private readonly ILogger logger;
    private readonly IConnectorConfigurator config;
    private readonly HttpClient client;
    private readonly IOcrConnector connector;

    public OcrController(
            ILogger logger, 
            IConnectorConfigurator config, 
            HttpClient client, 
            IOcrConnector connector) 
    {
        this.logger = logger;
        this.config = config;
        this.client = client;
        this.connector = connector;
    }
}

IOcrConnectorとHttpClientは依存関係がないことは明らかですが、他の2つについては心配しています。アクションによっては、構成から何かを読み取るためにIConfigurationが必要です。同様に、場合によってはロガーが必要になることもあります。

同じことが私のクラスの多くにも当てはまります-1つまたは2つの実際の依存関係、ロガーと構成、そして将来的には別の何か。

現時点では、シンプルで貧弱なServiceLocatorを使用しています。ILoggerとIConfigをコンストラクタに渡す代わりに、必要に応じて作成します。

protected ILogger Logger => this.logger 
    ?? (this.logger = ComponentFactory.GetLogger(this.GetType()));

protected IConnectorConfigurator Config => this.config 
    ?? (this.config = ComponentFactory.GetConfig());

「ファクトリー」は、以下のようなILoggerラッパーを返すだけなので、非常に単純です。

public static ILogger GetLogger(Type type)
{
    return new Log4NetLogger(type);
}

このファクトリは別のアセンブリに存在するため、具体的なILoggerを実際のプロジェクトから十分に切り離しているように思えます...?

ILoggerまたはIConfig(現在は組み込みのConfigurationManagerのラッパーにすぎません)を実行時などに交換するつもりはありませんが、必要に応じて、GetLogger(Type typeから返されるタイプを変更するだけです。 );方法。

では、このような適切なDIとServiceLocatorの組み合わせが理にかなっているかどうかを教えてください。他に考えられる別の方法は、IComponentFactoryをコンストラクタに渡すことです(グローバルに静的にアクセスする代わりに?)。しかし、それはまだサービスロケータであり、他の場所にのみ配置されています...

あなたの考えを教えてください

4
Bartosz

リンクされた例では、ブロガーは、依存関係が直接使用されるのではなく「チェーンを介して渡される」ため、コードをリファクタリングする必要があると主張しています。

DIとは関係なく、「Hoboパラメータ」コードの匂いがします。彼はOrderProcessorの代わりにOrderに注入することで修正できます

あなたの例では、あなたのコントローラーはおそらく直接ILoggerを使用しますか?したがって、同じ問題ではありません。

ムーブオーバーMVCコントローラーは、手順で設計する必要があるため、常に多くのサービスクラスを挿入する必要があります。

Orderパラメータを取るコントローラのメソッド呼び出しは、バインディングを介してリクエスト内の純粋な値データからそのオブジェクトを構築する必要があることを考慮してください。

Order入力パラメーターはこのパイプラインによって作成されるため、依存関係をコントローラーまたはバインダーに注入する必要があります。

Orderオブジェクトは参照によって渡されるのではなく、受信データから作成する必要があるため、ブロガーオブジェクトとまったく同じ方法で、パイプラインが呼び出しの依存関係設定として機能する必要があります。

3
Ewan

毎回新しいインスタンスを注入するか、同じインスタンスを再度注入するかにも違いがあります。後者のほうがはるかに高速です。

とはいえ、ロガーの場合、インスタンスを1つ作成して、事実上必要なときにいつでも挿入することができます。構成エンジンについても同様です。

1
Bernhard Hiller

ILoggerを渡すことは、Log4Netまたは多くのロガーラッパーの1つを使用しても、標準のログファクトリを介してロガーを非常に簡単に取得できるため、私には少々やり過ぎに思えます。

private static ILog logger = 
    LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

依存関係注入を使用してロギングの問題を実際に解決する方法はわかりません。 Microsoftの.Net Standardのロギングラッパーでさえ、適切な構成で実装をスワップアウトする機能を犠牲にすることなく、このタイプのロガーアクセスを可能にします。

本当に1つだけ残っています:IConnectorConfigurator。個人的には、それが本当の問題であることを証明するまでは、おそらくコンストラクターインジェクションを利用するでしょう。アプリでシングルトンになる可能性のあるタイプのように聞こえます。その場合、同じインスタンスがすべての実装に提供されます。

あなたは自問する必要があります:

  • 私は測定可能な問題に対処していますか? (つまり、パフォーマンス要件を満たすことができません)
  • 追加された問題の複雑さは、提案されたソリューションの複雑さの価値がありますか?
  • これはアプリのバグ修正の応答時間にどのように影響しますか?

コンストラクターパラメーターが12個以上ある場合は、コンポーネントの責任の分割を再考する必要があります。この場合、提案されたソリューションは、それが解決するよりも、複雑さと保守担当者への驚きを追加すると感じています。誤解しないでください。コンストラクターインジェクションの単純さから逸脱するための時間と場所があります。この場合、おそらくやり過ぎだと思います。

1
Berin Loritsch