web-dev-qa-db-ja.com

IoCコンテナーが使用されているときにオブジェクト間でデータを渡す方法は?

私は、私たちの建築家がほとんどすべてに依存性注入を使用することを決定したプロジェクトで作業しています。 IoCコンテナーを使用します。このパターンを使用しているときに遭遇している主な問題の1つは、「このビットのデータを、後で使用する別のオブジェクトに渡すにはどうすればよいですか?」です。

多くの場合、最も簡単な解決策は、特定のクラスを、特定のインジェクター/コンテナーで「シングルトン」としてマークすることです。これはいくつかの理由で悪いです。誰もが知っているように、シングルトンは悪であり、決して使用すべきではありません。 「シングルトン」としてマークされたクラスは、コードの異なる部分で使用される場合、実際には複数のインスタンスを持つ必要があります。クラスを「シングルトン」としてマークすると、クラスを「シングルトン」としてマークするという概念をサポートするインジェクター/コンテナーを使用する新しい依存関係が作成されます。

別の解決策は、クライアントが使用しているオブジェクトとクライアントの状態に基づいてインジェクターがオブジェクトをインスタンス化するときに、インジェクター/コンテナーがクロージャーを書き込むことでオブジェクトを作成する方法を変更するか、他の特定のロジックです。インジェクタ/コンテナに存在するこの機能の新しい依存関係を作成するため、これは悪いことです。また、インジェクト/コンテナがいくつかの異なるクラスに関係するようになるため、問題の分離に違反します。

この問題の使用例を以下に示します。

  1. ClassBのアプリケーションの早い段階でClassAを使用しています。 ClassAに保存して後で使用したい状態/データがあります。
  2. アプリケーションでさらにいくつかのことが起こります。 ClassAのオブジェクトがスタックのさらに下にあるか、スタックのその部分が存在せず、ClassAのオブジェクトへの参照がない可能性があります。 classBのメソッドは、まだ呼び出しスタックにある場合とない場合があります。
  3. アプリケーションの後半で、ClassCからClassAの状態/データを参照します。

更新-例を追加しています

class ClassA {
private:
    shared_ptr<ClassState> state;
    shared_ptr<ClassB> b;
public:
    ClassA(shared_ptr<ClassState> state, shared_ptr<ClassB> b) {
        this->state = state;
        this->b = b;
    }
    void run() {
        // do stuff
        if (true /* blah blah blah */) {
            state->setState(true);
        } else {
            state->setState(false);
        }
        // do more stuff
        b->run();
    }
};

class ClassB {
private:
    shared_ptr<ClassC> c;
public:
    ClassB(shared_ptr<ClassC> c) {
        this->c = c;
    }
    void run() {
        // do stuff
        c->run();
        // do more stuff
    }
};


class ClassC {
private:
    shared_ptr<ClassState> state;
public:
    ClassC(shared_ptr<ClassState> state) {
        this->state = state;
    }
    void run() {
        //This is where I want to use that data that was stored earlier
        // in the application
        if (state->getState()) {
            // do stuff
        }
    }
};

void main() {
    // Create whatever magicaly IOC dependency injector
    shared_ptr<ClassA> a = injector.create<ClassA> ();
    a->run();
}
5
Jacob Brown

必要なときにすぐに連絡を取り、物事を手に入れたいと思っているようです。これは、手続き型プログラミングの典型です。この方法で作業を続けると主張しても、言語、フレームワーク、パターン、またはツールで止めることはできません。

ただし、別の方法があります。 教えて、聞かないでください は、(通常はゲッターと静的参照を通じて行われるように)必要なものに手を差し伸べないで、必要なものだけを渡せるようにします。そうすれば、それを見つける方法を知る必要はありません。それはあなたを見つけます。

したがって、ClassCの何かが必要なClassAがあります。書き込むのではなくClassB.ClassA.getNeededThing()書き込み

class ClassC {
    String neededThing;
    public ClassC(String neededThing) { 
        this.neededThing = neededThing;
    }         
} 

このように、ClassCは、必要なものがなくなるまで存在しません。そして、必要なものを取得する方法を理解したのがClassAであるかClassBであるかは関係ありません。

これがオブジェクトの作成です。オブジェクトが作成された後も、相互に参照があれば、相互に通信できます。 anotherNeededThingClassCから必要とするClassAがあったとしましょう。

class ClassA {
    ClassC c;
    String someOtherNeededThing;
    public ClassA(ClassC c, String someOtherNeededThing){ 
        this.c = c;
        this.someOtherNeededThing = someOtherNeededThing;
    }
    public void timeToGiveTheThing() {
        c.doYourThingWith(someOtherNeededThing);
    }      
} 

このようにしてClassCClassAが存在することを知る必要すらありません。

このすべてを機能させるには、ファンシーなDIコンテナーが必要だと思うかもしれませんが、mainで実行できます(ある場合は、「構成ルート」を見つけてそこで実行します)。

int main() {
    ClassC c = new ClassC("Hello ");
    ClassA a = new ClassA("world", c);
    a.timeToGiveTheThing();
}

しかし、あなたはあなたが言うDIコンテナに行き詰まっていますか?いいでしょう、CをAの依存関係の1つとしてリストしてください。あなたがそれへの参照を得る限り、あなたはそれに話すことができます。これがいつ起こるべきかを知っている人があなたへの参照を持っている限り、あなたはあなたがすべきときにそれと話します。尋ねる必要はありません。

手続き型プログラミングを使用している場合これは、通常のプログラミング方法とは非常に異なります。それは別の方法です。このスタイルをオブジェクト指向プログラミングと呼びます。 OOP申し訳ありませんが、OOPを使用しているとは限りません。

これを理解すると、多くの引数を持つコンストラクターとメソッドシグネチャを作成していることに気付くでしょう。それが、プリミティブオブセッションとパラメータオブジェクトについて学ぶときです。パラメータオブジェクトとカプセル化されたオブジェクトを混同しないでください。

6
candied_orange

2.アプリケーションでさらにいくつかのことが起こります。 ClassAのオブジェクトがスタックのさらに下にあるか、スタックのその部分が存在せず、ClassAのオブジェクトへの参照がない可能性があります。 classBのメソッドは、まだ呼び出しスタックにある場合とない場合があります。

これが事実である場合、クラスAが消える前に、クラスCがそのデータにアクセスできるように、データを何らかの種類のデータとして保持する必要があります。

データが必要なため、依存性注入を使用してクラスAのインスタンスをクラスCに注入する場合は、クラスAが必要とするオブジェクトライフタイムのタイプを選択する必要があります。インスタンスごと、単一など.

そのクラスを存続させる必要がある場合は、DIコンテナーがそのクラスの単一のインスタンスを作成する必要があるようです。 DIコンテナーには、オブジェクトの存続期間を管理するためのオプションがあります。または、データを永続化してクラスCにデータを取得させ、クラスAから依存関係を削除させます。

1
Jon Raynor

問題を理解するのに苦労しています。特に、dependency injection(DI)に関連する問題をどのように見ているかを理解しています。あなたの問題は、DIの問題というよりも、コードベース内のアーキテクチャー/組織の問題のように30000ftビューから見えます。 DIがなければ、オブジェクトはとにかくこの問題を解決する必要があります。

オブジェクトが互いに通信するのに役立ついくつかの構造があります。

最も簡単なのは observer pattern を使用することです。ここで、1つのオブジェクトは、情報のソースSubject)およびoneまたはmoreオブジェクトが(Observer)をmessagessourceから放出されます。ソースがイベントを発行するたびに、オブザーバーはnotfiedです。これは、SubjectObserverの両方のライフタイムで機能します。また、どちらも参照を通じてリンクされているため、それらの寿命も同じです。

さらに分離したい場合は、いくつかのexternal真実のソースを使用する必要があります。

例えば.

  • 情報はファイルシステムにファイルの形で保持されます
  • 情報はデータベースのデータとして保持されます
  • 情報は実行中のプロセスに保持されます(メモリデータベース、 Genserver 、メッセージキューなど)

あなたのSubjectProducerになり、ObserverConsumerになり、通信はasynchronous


「このビットのデータを、後で使用する別のオブジェクトに渡すにはどうすればよいですか?」

同期Observer Patternまたは非同期

多くの場合、最も簡単な解決策は、特定のクラスを、特定のインジェクター/コンテナーで「シングルトン」としてマークすることです。

あなたが書いたものから、私はWebアプリケーションのコンテキストでDIを扱うと思います。 SpringアプリケーションでSingleton sayを使用しても問題はありません。シングルトンとは、このコンテキストでは、オブジェクトがリクエストごとにではなく1回だけ作成されることを意味します(cf Spring Beanスコープのクイックガイド )。競合状態のないアクセスを保証する場合、反対することは何もありません。

しかし、一方でprototype-注釈付きBeanは、消費されている間だけ存続し、要求されるたびに生成されます。これらはステートフルである可能性がありますが、その状態は存続期間とともに終了します。

別の解決策は、クライアントが使用しているオブジェクトとクライアントの状態に基づいてインジェクターがオブジェクトをインスタンス化するときに、インジェクター/コンテナーがクロージャーを書き込むことでオブジェクトを作成する方法を変更するか、他の特定のロジックです。

これは、シングルトンを使用する場合とそれほど変わりません。

この問題の使用例を以下に示します。

上記のように、オブジェクト間の通信を確立する必要があります。

1
Thomas Junk

シングルトンは、クラス間の依存関係を隠すため、グローバルな静的参照に関連付けられている場合は推奨されません。 DIコンテナーのコンテキストでのシングルトンは、実際には素晴らしいアイデアです。

  1. 特定のデータをスレッドセーフな方法で管理するシングルトンクラスを作成する
  2. データへの読み取り/書き込みアクセスを提供するシングルトンのパブリックインターフェイスとなる抽象クラスを作成します。
  3. コンストラクターインジェクションを使用して、パブリックインターフェイスを使用する必要があるオブジェクト間でデータを共有します。

シンプルで安全、そして理解しやすい。

1
mortalapeman