深いオブジェクト グラフと多くの依存関係を使用して手動 DI を適切に実行する方法
-
19-09-2019 - |
質問
この質問は何らかの方法で尋ねられたと思いますが、まだ理解していません。
私たちは GWT プロジェクトを行っていますが、私のプロジェクト リーダーは GIN/Guice を DI フレームワークとして使用することを許可していなかったので (新人プログラマーには理解できないだろうと彼は主張しました)、私は手動で DI を行うようにしています。
ディープオブジェクトグラフで問題が発生しました。UI からのオブジェクト階層は次のようになります。
アプリプレゼンター -> ダッシュボードプレゼンター -> ガジェットプレゼンター -> ガジェット構成プレゼンター
オブジェクト階層ツリーの下位にある GadgetConfigPresenter には、CustomerRepository、ProjectRepository、MandatorRepository などのいくつかの依存関係があります。
したがって、GadgetConfigPresenter を作成する GadgetPresenter にも、AppPresenter を作成するアプリのエントリ ポイントに至るまで、これらの依存関係などが存在します。
- これが手動 DI の本来の動作方法でしょうか?
- これは、必要ない場合でも起動時にすべての依存関係を作成することを意味するのではありませんか?
- ここでは GIN/Guice のような DI フレームワークが役に立ちますか?
解決
あなたはそれを書きます
GadgetConfigPresenter[.] を作成する GadgetPresenter
直接作成するのではなく、 GadgetConfigPresenter
インスタンス、 GadgetPresenter
すべき 抽象ファクトリーに依存する 作成できるのは GadgetConfigPresenter
そのためのインスタンス。これにより、内部の依存関係がプッシュされます。 GadgetConfigPresenter
工場へ。
使用する コンストラクターのインジェクション ずっと、あなたの 貧乏人のDI 配線は次のようになります (C# 構文については申し訳ありません)。
var customerRepository = new CustomerRepository(/*...*/);
var projectRepository = new ProjectRepository(/*...*/);
var mandatorRepository = new MandatorRepository(/*...*/);
var gadgetConfigPresenterFactory =
new GadgetConfigPresenterFactory(
customerRepository,
projectRepository,
mandatorRepository);
var gadgetPresenter = new GadgetPresenter(gadgetConfigPresenterFactory);
var dashboardPresenter = new DashboardPresenter(gadgetPresenter);
var appPresenter = new AppPresenter(dashboardPresenter);
私たちがどのようにして 依存関係の連鎖を断ち切る 多くの場合、各コンシューマの依存関係の数が大きくなりすぎないようにします。
原則として、これは、ブート時にすべての依存関係を作成する必要があることを意味します。 遅延読み込み戦略.
ライフタイムの管理などはまさに DI コンテナが非常に役立つ類のものですが、単に DI パターンと原則に従う.
ただし、全体的に見て、可能であれば DI コンテナをお勧めします。
他のヒント
あなたは、コンテキストインターフェイスを使用してDIを行うことができます。それはかなりまっすぐ進む難しいことではありません、と。
ContextインタフェースはGuiceのモジュールのコンフィギュレーションからすべてのバインディングを公開クラスです。
これは私がAppPresenter + DashboardPresenterが1つのパッケージにあるとGadgetPresenterとGadgetConfigPresenterは別のパッケージ内にあり、別の「コンテキスト」を必要としながら、一つの「コンテキスト」を必要とすることを仮定している。この例です。コンテキストの数、およびそれらをどのように処理するためには、完全にユーザに任されています。
/**
* The dependencies that need to be injected for package1
*/
public interface SomePackageContext {
GadgetPresenter getGadgetPresenter();
GadgetConfigPresenter getGadgetConfigPresenter();
}
/**
* The dependencies that need to be injected for package2
*/
public interface OtherPackageContext {
// These methods can take arguments..
AppPresenter getAppPresenter(Args..);
DashboardPresenter getDashboardPresenter(Args..);
}
/**
* All of the DI needed in our project.
*
* <p>We don't need the two interfaces above, we can put
* everything in this interface if we have a small
* project where layering is not a big issue.
*/
public interface PresenterContext
extends SomePackageContext, OtherPackageContext {
}
public class MockPresenterContext implements PresenterContext {
...
}
public class RealPresenterContext implements PresenterContext {
// This is similar to bind(...) in guice
public AppPresenter getAppPresenter(Args..) {
return new AppPresenter(this, otherargs...);
}
public DashboardPresenter getDashboardPresenter(Args..) {
return new DashboardPresenter(this, otherargs...);
}
public GadgetPresenter getGadgetPresenter() {
return new GadgetPresenter(this);
}
public GadgetConfigPresenter getGadgetConfigPresenter() {
return new GadgetConfigPresenter();
}
}
public class DashboardPresenter {
// @Inject
private final GadgetPresenter gadgetPresenter;
/*
* We inject everything using the SomePackageContext.
*/
public DashboardPresenter(SomePackageContext ctxt) {
this.gadgetPresenter = ctxt.getGadgetPresenter();
}
}