私は、私たちの建築家がほとんどすべてに依存性注入を使用することを決定したプロジェクトで作業しています。 IoCコンテナーを使用します。このパターンを使用しているときに遭遇している主な問題の1つは、「このビットのデータを、後で使用する別のオブジェクトに渡すにはどうすればよいですか?」です。
多くの場合、最も簡単な解決策は、特定のクラスを、特定のインジェクター/コンテナーで「シングルトン」としてマークすることです。これはいくつかの理由で悪いです。誰もが知っているように、シングルトンは悪であり、決して使用すべきではありません。 「シングルトン」としてマークされたクラスは、コードの異なる部分で使用される場合、実際には複数のインスタンスを持つ必要があります。クラスを「シングルトン」としてマークすると、クラスを「シングルトン」としてマークするという概念をサポートするインジェクター/コンテナーを使用する新しい依存関係が作成されます。
別の解決策は、クライアントが使用しているオブジェクトとクライアントの状態に基づいてインジェクターがオブジェクトをインスタンス化するときに、インジェクター/コンテナーがクロージャーを書き込むことでオブジェクトを作成する方法を変更するか、他の特定のロジックです。インジェクタ/コンテナに存在するこの機能の新しい依存関係を作成するため、これは悪いことです。また、インジェクト/コンテナがいくつかの異なるクラスに関係するようになるため、問題の分離に違反します。
この問題の使用例を以下に示します。
更新-例を追加しています
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();
}
必要なときにすぐに連絡を取り、物事を手に入れたいと思っているようです。これは、手続き型プログラミングの典型です。この方法で作業を続けると主張しても、言語、フレームワーク、パターン、またはツールで止めることはできません。
ただし、別の方法があります。 教えて、聞かないでください は、(通常はゲッターと静的参照を通じて行われるように)必要なものに手を差し伸べないで、必要なものだけを渡せるようにします。そうすれば、それを見つける方法を知る必要はありません。それはあなたを見つけます。
したがって、ClassC
の何かが必要なClassA
があります。書き込むのではなくClassB.ClassA.getNeededThing()
書き込み
class ClassC {
String neededThing;
public ClassC(String neededThing) {
this.neededThing = neededThing;
}
}
このように、ClassC
は、必要なものがなくなるまで存在しません。そして、必要なものを取得する方法を理解したのがClassA
であるかClassB
であるかは関係ありません。
これがオブジェクトの作成です。オブジェクトが作成された後も、相互に参照があれば、相互に通信できます。 anotherNeededThing
がClassC
から必要とする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);
}
}
このようにしてClassC
はClassA
が存在することを知る必要すらありません。
このすべてを機能させるには、ファンシーなDIコンテナーが必要だと思うかもしれませんが、main
で実行できます(ある場合は、「構成ルート」を見つけてそこで実行します)。
int main() {
ClassC c = new ClassC("Hello ");
ClassA a = new ClassA("world", c);
a.timeToGiveTheThing();
}
しかし、あなたはあなたが言うDIコンテナに行き詰まっていますか?いいでしょう、CをAの依存関係の1つとしてリストしてください。あなたがそれへの参照を得る限り、あなたはそれに話すことができます。これがいつ起こるべきかを知っている人があなたへの参照を持っている限り、あなたはあなたがすべきときにそれと話します。尋ねる必要はありません。
手続き型プログラミングを使用している場合これは、通常のプログラミング方法とは非常に異なります。それは別の方法です。このスタイルをオブジェクト指向プログラミングと呼びます。 OOP申し訳ありませんが、OOPを使用しているとは限りません。
これを理解すると、多くの引数を持つコンストラクターとメソッドシグネチャを作成していることに気付くでしょう。それが、プリミティブオブセッションとパラメータオブジェクトについて学ぶときです。パラメータオブジェクトとカプセル化されたオブジェクトを混同しないでください。
2.アプリケーションでさらにいくつかのことが起こります。 ClassAのオブジェクトがスタックのさらに下にあるか、スタックのその部分が存在せず、ClassAのオブジェクトへの参照がない可能性があります。 classBのメソッドは、まだ呼び出しスタックにある場合とない場合があります。
これが事実である場合、クラスAが消える前に、クラスCがそのデータにアクセスできるように、データを何らかの種類のデータとして保持する必要があります。
データが必要なため、依存性注入を使用してクラスAのインスタンスをクラスCに注入する場合は、クラスAが必要とするオブジェクトライフタイムのタイプを選択する必要があります。インスタンスごと、単一など.
そのクラスを存続させる必要がある場合は、DIコンテナーがそのクラスの単一のインスタンスを作成する必要があるようです。 DIコンテナーには、オブジェクトの存続期間を管理するためのオプションがあります。または、データを永続化してクラスCにデータを取得させ、クラスAから依存関係を削除させます。
問題を理解するのに苦労しています。特に、dependency injection
(DI)に関連する問題をどのように見ているかを理解しています。あなたの問題は、DIの問題というよりも、コードベース内のアーキテクチャー/組織の問題のように30000ftビューから見えます。 DIがなければ、オブジェクトはとにかくこの問題を解決する必要があります。
オブジェクトが互いに通信するのに役立ついくつかの構造があります。
最も簡単なのは observer pattern を使用することです。ここで、1つのオブジェクトは、情報のソース(Subject
)およびoneまたはmoreオブジェクトが(Observer
)をmessagessourceから放出されます。ソースがイベントを発行するたびに、オブザーバーはnotfied
です。これは、SubjectとObserverの両方のライフタイムで機能します。また、どちらも参照を通じてリンクされているため、それらの寿命も同じです。
さらに分離したい場合は、いくつかのexternal真実のソースを使用する必要があります。
例えば.
あなたのSubject
はProducer
になり、Observer
はConsumer
になり、通信はasynchronous。
「このビットのデータを、後で使用する別のオブジェクトに渡すにはどうすればよいですか?」
同期Observer Patternまたは非同期。
多くの場合、最も簡単な解決策は、特定のクラスを、特定のインジェクター/コンテナーで「シングルトン」としてマークすることです。
あなたが書いたものから、私はWebアプリケーションのコンテキストでDIを扱うと思います。 SpringアプリケーションでSingleton
sayを使用しても問題はありません。シングルトンとは、このコンテキストでは、オブジェクトがリクエストごとにではなく1回だけ作成されることを意味します(cf Spring Beanスコープのクイックガイド )。競合状態のないアクセスを保証する場合、反対することは何もありません。
しかし、一方でprototype
-注釈付きBeanは、消費されている間だけ存続し、要求されるたびに生成されます。これらはステートフルである可能性がありますが、その状態は存続期間とともに終了します。
別の解決策は、クライアントが使用しているオブジェクトとクライアントの状態に基づいてインジェクターがオブジェクトをインスタンス化するときに、インジェクター/コンテナーがクロージャーを書き込むことでオブジェクトを作成する方法を変更するか、他の特定のロジックです。
これは、シングルトンを使用する場合とそれほど変わりません。
この問題の使用例を以下に示します。
上記のように、オブジェクト間の通信を確立する必要があります。
シングルトンは、クラス間の依存関係を隠すため、グローバルな静的参照に関連付けられている場合は推奨されません。 DIコンテナーのコンテキストでのシングルトンは、実際には素晴らしいアイデアです。
シンプルで安全、そして理解しやすい。