web-dev-qa-db-ja.com

階層化アーキテクチャでの依存性注入と依存性ルックアップ

ビジネスロジックが、永続ストレージ、ユーザーインターフェイス、さまざまな(ベンダー)リモートサービスのインターフェイスなど、他のすべてのレイヤーからパッケージで分離されている階層化アーキテクチャーがある場合、次の方法で依存関係を管理することを考えています。

ライブラリ内では、すべての依存関係はコンストラクタを介して手動で注入されます。これにはテストがより明確になり、ドキュメンテーションソースとしての価値が高まるという利点があります(私の意見では大きいです)。

他のレイヤーとプラグインについては、依存関係のルックアップに使用されるオブジェクトを1つだけ手動で注入すると、モックが簡単になります。すべてのサブシステムにオブジェクトが1つしかないのではないかと思います。本当に特定の小さなインターフェースと、それらのインターフェースの1つ以上を実装するファクトリインスタンスについて考えているだけです。そのようなファクトリーが実装するインターフェースの数は実装の詳細であり、すべてのインターフェースを実装する1つのファクトリーのみから始め、システムが進化するにつれて、他のファクトリーを引き出してそれに応じてリファクタリングします(YAGNI)。 interface segregationのおかげで、インターフェース(つまり、コンストラクターのシグネチャ)は、リファクタリング前の状態のままになります。

ビジネスライブラリは、コマンドを介して外部からコマンドを取得します(DDDと考えてください)。

  1. これについてどう思う?その他の長所と短所は大歓迎です!
  2. どのような状況で、依存関係を管理するためのその他の戦略として、どのようなものを好みますか?その理由は何ですか?
2
Flavius

あなたはSpringの ApplicationContextAware に似た(しかし似ていない)ものを探していますが、ここでApplicationContext(これはBeanFactoryです)をセッター経由で渡すので、後で必要なBeanを取得できます。

依存関係を注入する必要があるだけの場合は、一般に悪い考えです。

  • 使用する依存関係は明示されていません。したがって、実際にモックするのは難しいより簡単ではありません。クラスのコード全体、おそらくそのスーパークラスとサブクラスのコード全体、継承が発生した場合。
  • 文字列キーで依存関係をクエリする場合、タイプセーフではなく、そのすべての否定的な結果が伴います。ただし、それに対処するための特定のインターフェースがあるとすると(UserRepositoryFactoryのようなメソッドを持つgetUserRepositoryのようです)、タイプセーフになりますが、私はしません中程度のgetter/wrapper/injector-objectsを使用せずに依存関係を注入する方がはるかに簡単なため、このソリューションの利点を確認してください。それは、DI機能の複製にすぎないと思います。

プラグインシステムを作成する場合は、いくつかのSomethingPluginインターフェースを実装する「愚かな」プラグインを使用し、依存関係が注入されることを期待します(コンストラクターまたはセッターを介して、実際には重要ではありません)。外部の何かについての事前の知識なしにシステム。

例:

_public class UserRatingPlugin implements GenericPlugin,
        UserRepositoryAwarePlugin, PostResositoryAwarePlugin {

    // here are dependencies
    private UserRepository userRepository;
    private PostRepository postRepository

    @Override // from UserRepositoryAwarePlugin
    public void setUserRepository(UserRepository ur) {
        userRepository = ur;
    }

    @Override // from PostResositoryAwarePlugin
    public void setPostRepository(PostRepository pr) {
        postRepository = pr;
    }

    @Override // from GenericPlugin
    // this should be required to make this class a plugin
    public void doPluginMagic() { /* ... */ }
}
_

次に、このプラグインを(設定ファイル経由で)登録し、「システムレベル」のコードのどこかにこのクラスをインスタンス化し、依存関係を設定し(この部分は、Springまたは類似のDIコンテナによって自動化できます)、doPluginMagic()

このようにして、まだ特定の小さなインターフェースを持っていますが、プラグインAPIの一部として、全体を単純化すると思います。タイプ宣言を見ればプラグインがアクセスできるものを確認でき、追加の「ファクトリー」を作成する必要はありません。

2
scriptin

依存関係のルックアップは、サービスロケーターが静的である場合が多いことを除いて、サービスロケーターパターンに非常に似ています。

通常の依存関係注入と比較した場合のそのような依存関係ルックアップの利点は次のとおりです。

  • 多くの依存関係が必要な場合は、数十(多くの場合は必須)の引数を取るコンストラクターは見つかりません。これにより、コードが短くなり、読み書きしやすくなります。

  • オブジェクトが追加の依存関係に依存する必要がある場合(または以前に使用された依存関係が必要ない場合)、コンストラクターの定義を変更する必要はありません。つまり、時間の経過に伴うコード変更が少なくなります。

ただし、欠点があります。

  • 依存関係は制御不能です。コンストラクターを介して依存関係が注入されると、コンストラクターを調べることで、クラスが必要とする依存関係がすぐにわかります。ルックアップ(またはサービスロケーター)のみが存在する場合、クラスは依存関係を[〜#〜] n [〜#〜]依存関係に依存しているだけであり、追加情報はありません。

    また、これらのクラスを使用することも困難になります。ユニットテストのコンテキストにいるとします。ルックアップでモックする必要があるオブジェクトはどれですか。問題は依存性注入にすでに存在します(テストされたメソッドは必ずしもクラスによって要求されたすべての依存関係を使用する必要がないため)、ルックアップを使用すると、潜在的に使用できる依存関係が多くなり、検索が大きくなります。

    パススルー(つまり、クラスAが依存関係を要求して、クラスAが呼び出す別のクラスBに渡す場合)を渡すと、クラスBが使用する依存関係を確認できないため、このタスクはさらに悪化する可能性があります。それにルックアップ。

3