クラスBを作成するクラスAがあるとします。クラスBはクラスCに依存し、クラスCはクラスDに依存します。クラスDの作成を担当するのは誰ですか?
あなたはステップをジャンプしています。疎結合と例外安全のために最適化された一連の規則を検討してください。ルールは次のようになります。
コード(std :: move呼び出しは簡略化のために省略されています):
struct D { int dummy; };
struct C { D d; };
struct B { C c; }
struct A { B make_b(C c) {return B{c}; };
そのようなシステムでは、「だれがDを作成するか」は無関係です。なぜなら、make_bを呼び出すときは、DではなくCが必要だからです。
クライアントコード:
A a; // factory instance
// construct a B instance:
D d;
C c {d};
B = a.make_b(c);
ここで、Dはクライアントコードによって作成されます。当然、このコードが複数回繰り返されている場合は、自由に関数に抽出できます(上記のR2を参照)。
B make_b_from_d(D& d) // you should probably inject A instance here as well
{
C c {d};
A a;
return a.make_b(c);
}
make_bの定義をスキップし(R1を無視)、次のようにコードを直接作成する自然な傾向があります。
struct D { int dummy; };
struct C { D d; };
struct B { C c; }
struct A { B make_b(D d) { C c; return B{c}; }; // make B from D directly
この場合、次の問題があります。
モノリシックコードがあります。クライアントコードで、既存のCからBを作成する必要がある場合は、make_bを使用できません。新しいファクトリを作成するか、make_bの定義と、古いmake_bを使用するすべてのクライアントコードを作成する必要があります。
ソースを見ると、依存関係の見方が混乱しています。ソースを見ると、実際にはCだけが必要な場合でも、Dインスタンスが必要であると考えるようになります。
例:
void sub_optimal_solution(C& existent_c) {
// you cannot create a B here using existent_C, because your A::make_b
// takes a D parameter; B's construction doesn't actually need a D
// but you cannot see that at all if you just have:
// struct A { B make_b(D d); };
}
- を省略すると
struct A { B make_b(C c); }
結合が大幅に増加します。Aは(Cだけでなく)BとCの両方の定義を知る必要があります。また、ファクトリメソッド(R1)の定義のステップをスキップしたため、プロジェクトに課されたA、B、C、Dを使用するクライアントコードにも制限があります。
TLDR:つまり、最も外側の依存関係をファクトリに渡さず、最も近い依存関係をファクトリに渡します。これにより、コードが堅牢かつ簡単に変更可能になり、提起された質問(「Dを作成した人」)がmake_bの実装に関する無関係な質問にレンダリングされます(make_bはDを受信しなくなったため、より直接的な依存関係-C-となるため、これはmake_b)のパラメーターとして挿入されます。