web-dev-qa-db-ja.com

依存性注入でコンテキストを渡す

私が取り組んでいるプロジェクトでは、Guiceを使用して、Dependency Injectionを可能な限り実行しようとしています。ただし、問題が1つあります。私のオブジェクトの多くはオブジェクトContextに依存しています。これは不変オブジェクトですが、他のほとんどのオブジェクトが適切に機能するために必要ないくつかの情報があります。同じContext内ですべて動作しているクラスの場合は変更されませんが、アプリケーションは同じスコープ内で同時に異なるContextsをサポートできる必要があります。私の問題を解決する3つの方法を考えることができます。

  1. 現在、AssistedInjectを使用して多くのコードを記述していることに気づきました。これには、ほとんど常にFactory.create(Context c)のようなメソッドがあります。 Contextに依存するすべてのコードは、他の単純なコードであっても、この方法で作成されています。

  2. Providersの使用を検討し始め、factory.create(context)のすべての行をobj = provider.get(); obj.setContext(c);に変更しますが、両方の呼び出しを行うためにコードに依存したくありません。コンパイラーがすべてのオブジェクトがcontextであること、およびcontextが変更できない(変更されない)ことをコンパイラーが確認するのが好きです。

  3. 私の調査では、このスレッドを指摘しました: https://stackoverflow.com/questions/16316719/guice-assisted-injection-deeper-down-the-dependency-hierarchy 各ペアリングのアイデアContextを1行childInjectorに変換しますbind(Context.class).toInstance(ContextFactory.makeInstance(args))ですが、すべてが正しい子インジェクターを使用していることを確認する方法がわかりません。これは最善のようですが、そのスレッド(これは私のシナリオではありません)以外は、どうすればよいのか、自分の状況に適しているのかどうかわかりません。

オプション3で実行できることの sscce を次に示します。

public class GuiceChildTest {
    public static void main(String... args) {
        Injector inj = Guice.createInjector(new ParentModule());
        Injector fooChildOne = inj.createChildInjector(new FooModule(new Foo(10)));
        Injector fooChildTwo = inj.createChildInjector(new FooModule(new Foo(20)));

        Bar bar = fooChildOne.getInstance(Bar.class);
        System.out.println(bar.baz());

        bar = fooChildTwo.getInstance(Bar.class);
        System.out.println(bar.baz());
    }

    private static class ParentModule extends AbstractModule {
        @Override
        protected void configure() {
        }
    }

    private static class Bar {
        private MyInterface intf;
        @Inject
        public Bar(MyInterface in) {
            this.intf = in;
        }

        public int baz() {
            return intf.doSomething();
        }
    }

    private static class FooModule extends AbstractModule {
        private Foo f;
        public FooModule(Foo f) {
            this.f = f;
        }

        @Override
        protected void configure() {
            bind(MyInterface.class).toInstance(f);
        }   
    }   

    private static class Foo implements MyInterface {
        private int i;
        public Foo(int i) {
            this.i = i;
        }
        @Override
        public int doSomething() {
            return i;
        }
    }

    private static interface MyInterface {
        int doSomething();
    }
}
6
durron597

ディペンデンシーインジェクション の基本に取り組み、 ディペンデンシーインジェクションの原則 を簡単に確認してみましょう。

この原則に従うと、高レベルのポリシー設定モジュールから低レベルの依存関係モジュールに確立された従来の依存関係が反転(つまり、反転)するため、高レベルモジュールは低レベルモジュールの実装の詳細とは無関係になります。原則は

  • 高レベルのモジュールは、低レベルのモジュールに依存するべきではありません。どちらも抽象化に依存する必要があります。
  • 抽象化は詳細に依存すべきではありません。詳細は抽象化に依存する必要があります。

あなたが言及した最初の2つのアプローチは、DIの原則に完全に従っていない可能性があります。これは、高次のオブジェクトが持つべきContextのタイプを知る必要があるためです。

そして続ける前に、狂信的なDI支持者を怒らせる可能性のあるバイアスを認めるべきです:

DIは、原則に過度に重点を置き、熱心に執着することができるため、DIが動作する環境に適した保守可能なコードを作成する際に邪魔になります。そして、それは必ずしもDIに対するDigではなく、盲目的に適用されることになるすべての技術に対するものです。 あなたが盲目的にDIを適用していると言っているわけではありませんが、そのカテゴリに分類される人はたくさんいます。

そのような主張に過度に敏感な人々を保護するために使用されるネタバレ

したがって、メソッド1および2を「悪い」と呼ぶ前に、現在のニーズを満たしているかどうかを確認する必要があります。これらのアプローチには、コピーされたボイラープレートコードの測定が含まれます。テンプレートを頻繁に変更する必要がない場合は、これで問題ありません。

同様に、オブジェクトが使用できるコンテキストのタイプが1つのみの場合、この場合、DIについて心配する必要がない必要がないため、オブジェクトに注入する必要がある何か他のもの。 「しかし、テストについてはどうですか?!」その実用的な主張に対する通常のレトルトは、「それらのオブジェクトはテストに異なるコンテキストを使用できますか?」という等しく実用的な答えで答えられます。はいの場合、DIを使用します。そうでない場合、...続行します。

3番目のオプションを提案することで、ボイラープレートコードから離れ、DIアプローチに近づくことができます。あなたのためらいは次のようです:

しかし、すべてが正しい子インジェクターを使用していることを確認する方法がわかりません

その課題の解決策は、コンテキストの作成を Factory 内にラップし、高次オブジェクトが何を呼び出す必要があるかを認識しないようにすることだと思います。

だから私があなたのコードサンプルを正しく理解したなら、これの代わりに:

Bar bar = fooChildOne.getInstance(Bar.class);

あなたは次のようなものになるでしょう:

Bar bar = new Bar(ContextFactory.getInstance(Bar.class));

提案では、ContextFactoryから返されたコンテキストをBarのコンストラクターに渡します。そうかも知れない 1 その例を次のように単純化するのは簡単です:

public class Bar(){
    Bar(){
        return Bar(ContextFactory.getInstance(Bar.class));
    }
}

または:

public class Bar(){
    Bar(){
        this.Context = ContextFactory.getInstance(Bar.class);
    }
}

1私のJavaは錆びているので、最初の例のようにコンストラクタをチェーンできるかどうかわかりません。

また、ContextFactory内のリフレクションを使用してBarの正しいコンテキストを決定するか、オプションでContextFactoryに追加のパラメーターを渡してBarは、通常の操作で複数のタイプのコンテキストを処理できます。

TL; DR

提案した3番目のオプションを使用して、コンテキストの作成を Factory でラップします。

4
user53019