クラス階層コンストラクターのパラメーターが最も下位のクラスに多くのパラメーターを強制する状況を処理するための設計パターンはありますか?
言語が重要であれば、理想的にはこれはC++で行われます。次の一般的な例を検討してください。
class dependency1;
class dependency2;
class dependency3;
class ClassA
{
public:
ClassA(dependency1* dep1) { }
};
class ClassB : public ClassA
{
public:
ClassB(dependency1* dep1, dependency2* dep2)
: ClassA(dep1){ }
};
class ClassC : public ClassB
{
public:
ClassC(dependency1* dep1, dependency2* dep2, dependency3* dep3)
: ClassB(dep1, dep2) { }
};
階層内の各クラスは固有の依存関係を追加し、2つのパラメーターは基本クラスへの受け渡しにのみ使用されますが、最終的には最後のクラスctorに3つのパラメーターが生じます。最後のクラスに11個のパラメーターがあるような、もっと複雑な例を想像できます...
一般にこれに答えるのは難しいかもしれませんが、この問題への一般的な取り組み方を知りたいです。
これに近い唯一のことは、パラメーターが多すぎる状況でビルダーパターンを使用することですが、このケースには適合しないようです(または、クラス階層を単純化するのにどのように役立つかわかりません)。
重複する回答にリダイレクトしてください。見つけられませんでしたが、既に質問されているはずです。
私の見解では、直接が11の要素に依存しているクラスがある場合、多すぎると思われます。
次に、サブクラスの代わりに、ClassC
にClassB
型のメンバーがあったClassA
型のメンバーがあった場合にどうなるかを考えます。今回は違いがある以外は、同じ問題が残っているようです。 ClassC
はClassB
に直接依存するべきではありません。代わりに、(周囲のインターフェース)ClassB
をパラメーターとして取り込む必要があります。これで、ClassC
は、ClassB
の依存関係を追加のパラメーターとして取得する必要がなくなりました。 ClassB
とClassA
も同様です。この時点では、各クラスは直接必要な依存関係のみを受け入れます。これには、クラスの結合度と複雑度を示すという利点があります。
依存関係注入コンテナを使用している場合、またはクラスを手動で関連付けている場合、最終的には各クラスがスタンドアロンになります。たとえば、各クラスは、他のクラスが存在しなくてもコンパイルできます(インターフェイスを使用した場合[抽象基本クラス])。ハードな依存関係はありません。実際の依存関係グラフの作成は、トップレベルの手順で行われます。依存関係注入コンテナによって自動的に、または手動で。
サブクラス化に戻ると、コードの設計が不十分でない限り、説明した問題に遭遇することはありません。上記で提案したように、そのようなコードに実行し始めた場合は、何か間違っていることを示しているため、リファクタリングする必要があります。 ClassC
が7レベルのサブクラスであるか、最上位のクラスであるかは関係ありません。結局のところ、ClassC
には、警告サインとしてとるべき依存関係がすべて必要です。個人的には、深い階層を避けることをお勧めしますが、階層が問題ではないことを明確にしたいと思います。多数の依存関係があります。この場合、C++は、スーパークラス(確かにかなり珍しい機能)によってクラスをパラメーター化できる言語ではありません。これは、コード構造化メカニズムとしての構成ではなく、継承の使用に対する別の引数を提供します。スーパークラスはサブクラスの強い依存関係です。
1つの可能性は、ビルダーのようなオブジェクトの階層を使用することです。
class dependency1;
class dependency2;
class dependency3;
class ClassA {
public:
struct Builder {
dependency1 *dep1 = 0;
};
ClassA(const Builder &builder) { ... }
};
class ClassB : public ClassA {
public:
struct Builder : ClassA::Builder {
dependency2 *dep2 = 0;
};
ClassB(const Builder &builder) : ClassA(builder) { ... }
};
class ClassC : public ClassB {
public:
struct Builder : ClassB::Builder {
dependency3 *dep3 = 0;
};
ClassC(const Builder &builder) : ClassB(builder) { ... }
};
次のように使用します。
ClassC::Builder builder;
builder.dep1 = dep1;
builder.dep2 = dep2;
builder.dep3 = dep3;
ClassC c(builder);
これは、典型的なビルダーの Fluent Interface 形式を使用しません。これは、階層で機能させるためにいくつかのトリックが必要になるため、おそらく価値がありません。