3

C++ での並列継承に関する問題を解決しようとしています。これについて私が見つけた最も近い質問は、あるツリーのクラスが別のツリーのクラスのコンテナを持つ並列継承ツリーですが、私の問題には完全には答えません。

Model、Trainer、InstanceProcessor (IP) の 3 つのクラスで構成されるモデルがあります。これらのクラスには、それぞれモデル データ、トレーニング関数、およびモデル処理コードが含まれています。現在、このモデルには約 10 の異なるバージョンがあり、多くのコードが重複していますが、いくつかの違いがあり、何らかの形式の並列継承を作成しています。

Model - ModelA, ModelB, ModelC, ...
Trainer - TrainerA, TrainerB, TrainerC
IP - IPA, IPB, IPC

コードの本体では、Model* ポインターを使用して、解析された引数に応じて特定のモデルにアクセスします。

モデルとトレーナーの両方が IP の複数の短期間のインスタンスを必要とし、モデルはさらにトレーナーの永続的なインスタンスを必要とします。

私の現在の実装では、3 つの基本クラスといくつかの仮想関数を使用し、これらの基本クラスから継承する特定のクラスをモデル化します。これには、多くのキャストを使用する必要があります (たとえば、model.h の Trainer* トレーナーから、特定のニーズのために TrainerA に)。

これを実装するよりエレガントな方法があると思いますが(テンプレート/インターフェースを使用しますか?)、誰かが私を正しい方向に向けることができるかどうか疑問に思っていましたか? ありがとう!


編集:以下の回答からポイントを明確にするために、複雑さの1つは、私が欲しいという事実にあります。関数 basic_train() を持つトレーナー クラス:

Trainer::basicTraining() {
    ...
    IP* ip = new IP(some args);
    ip->doStuff();
    ...
}

ここで、使用するトレーナーのタイプに応じて、適切な IP が作成されます。その関数の残りの部分は、TrainerA から TrainerB インスタンスに変更されません。

4

3 に答える 3

2

2 つの別個の階層のメンバーが密結合している場合、継承はあまり役に立ちません。継承は、実際にはこれが機能しない場合でも、組み合わせ戦略が可能であることを約束ModelAします (つまり、 withTrainerBと anを使用しますIPC)。

ただし、認識すべき非常に重要なことの 1 つは、モデル、トレーナー、および IP が相互に提示するインターフェイスは、それらがまとめてメイン アプリケーションに提示するインターフェイスとは異なる可能性があるということです。メイン アプリケーションはそれらを均一なオブジェクトのグループとして認識します。 、モデル/トレーナー/IP の各サブグループ (つまりA、グループ) はBC同じグループのメンバーを高度に専門化されたものとして扱います。したがって、継承は継承階層の個々の部分には悪いかもしれませんが、メイン プログラムには良いかもしれません。

これにより、抽象的なファクトリパターンを使用する実行可能なソリューションが得られます。メイン アプリケーションは、渡されたものに基づいてFactoryAFactoryB、またはを与えることができる「ファクトリのファクトリ」を取得します。それぞれが、、およびオブジェクトを生成し、それらを共通のスーパークラス (つまり、、および) としてメイン プログラムに提示します。FactoryCFactoryXModelXTrainerXIPXModelTrainerIP

ただし、ファクトリの内部では、実装オブジェクトは「対応する」オブジェクトの正確な型を認識して作成されます。たとえば、ModelAが で構成されてTrainerいる場合、タイプのオブジェクトは取得されません。Trainer取得されるのはTrainerAです。FactoryA作成するオブジェクト間の依存関係を認識しているため、正しいタイプのオブジェクトを問題なく提供できます。同時に、メイン プログラムはこの特殊化を認識しておらず、部分 ( ModelATrainerA、およびIPA) は一般化をほとんど認識していません。

于 2013-10-16T20:07:26.347 に答える
1

基本的に、キャストは脆いので悪いです (コードを更新する必要があるときに更新するのを忘れてしまいます)。むしろ、ポリモーフィズムを利用する必要があります。インターフェースを使用するという最初のアイデアとおそらく一致するビジター パターンを見たいと思うかもしれません。

于 2013-10-16T19:56:27.480 に答える
0

より抽象的な言葉で言えば、継承に関してすでに固定されているクラスのセットがあり、ユースケースに応じてそれぞれの動作を変えたいとします。

このような状況では、ほとんどの場合、依存性注入またはポリシーが役立ちます。メソッド呼び出しごとにこれを行うか、オブジェクトの作成時に決定することができます。

最初のケースでは、メソッドの動作を変更する何か (以下を参照) を引数として渡します。

2 番目のケースでは、その引数をコンストラクターに渡し、オブジェクトはその「何か」を格納する必要があります。

「何か」は、値の固定セット (列挙型など) のように単純なものにすることも、コールバック/ファンクター/ラムダまたはオブジェクトにすることもできます。快適なものを選択してください。ポイントは、オブジェクトがすべて同じクラスであっても、オブジェクトの動作を変更できることです。

于 2013-10-16T19:50:46.040 に答える