多重継承を使用するのは良い概念ですか、それとも代わりに他のことを行うことができますか?
多重継承(MIと略される)はにおいがします。これは、通常、それが悪い理由で行われたことを意味し、メンテナの前で吹き返します。
これは継承にも当てはまります。したがって、多重継承にも当てはまります。
あなたのオブジェクトは本当に別のものから継承する必要がありますか?ACar
はEngine
、からも、からも継承する必要はありませんWheel
。ACar
にはEngine
と4つありWheel
ます。
構成の代わりに多重継承を使用してこれらの問題を解決する場合は、何か問題があります。
通常、あなたはクラス持ちA
、その後、B
とC
の両方から継承しますA
。そして(理由を聞かないでください)そして誰かがそれD
からB
との両方を継承しなければならないと決定しC
ます。
私はこの種の問題に88年に2回遭遇しましたが、次の理由で見るのはおもしろいです。
D
両方から継承すべきではありませんでした)。B
C
C
A
が孫クラスに2回存在していたため、メンテナがいくら払っていたD
ので、1つの親フィールドA::field
を更新するということは、それを2回更新するか(とを介しB::field
てC::field
)、後で何かが黙って間違ってクラッシュすることを意味しました。(でポインタを新しくしB::field
、削除しC::field
ます...)C ++でキーワードvirtualを使用して継承を修飾すると、これが必要なものでない場合は上記の二重レイアウトを回避できますが、とにかく、私の経験では、おそらく何か間違ったことをしています...
オブジェクト階層では、階層をグラフではなくツリー(ノードの親が1つ)として保持するようにしてください。
C ++でのDiamondof Dreadの本当の問題(デザインが健全であると仮定して-コードをレビューしてください!)は、選択を行う必要があることです:
A
がレイアウトに2回存在することが望ましいですか、それはどういう意味ですか?はいの場合は、必ず2回継承します。この選択は問題に固有のものであり、C ++では、他の言語とは異なり、言語レベルで設計を強制することなく実際に行うことができます。
しかし、すべての力と同様に、その力には責任が伴います。設計をレビューしてもらいます。
上記のDiamondof Dreadに遭遇しないため、0個または1個の具象クラスの多重継承、および0個以上のインターフェースは通常問題ありません。実際、これがJavaで行われる方法です。
通常、Cが継承しA
、B
ユーザーがC
それがであるかのようにA
、および/またはであるかのように使用できることを意味しますB
。
C ++では、インターフェイスは次のような抽象クラスです。
ゼロから1つの実オブジェクトへの多重継承、およびゼロ以上のインターフェースは「臭い」とは見なされません(少なくともそれほど多くはありません)。
まず、実際の基準は状態を持たない(つまり、を除いてメンバー変数を持たない)ことであるため、NVIパターンを使用してインターフェイスを作成できますthis
。あなたの抽象的なインターフェースのポイントは、契約を公開することです(「あなたは私をこのように、そしてこのように呼ぶことができます」)、それ以上でもそれ以下でもありません。抽象仮想メソッドのみを持つことの制限は、義務ではなく、設計上の選択であるべきです。
第2に、C ++では、(追加のコスト/間接参照があっても)抽象インターフェースから仮想的に継承することは理にかなっています。そうしないと、インターフェイスの継承が階層内に複数回表示されるため、あいまいさが生じます。
第三には、オブジェクト指向は素晴らしいですが、それはないだけ真実そこTM C ++で。適切なツールを使用し、さまざまな種類のソリューションを提供するC ++の他のパラダイムがあることを常に忘れないでください。
時々、はい。
通常、あなたのC
クラスから継承しているA
とB
し、A
そしてB
2つの無関係なオブジェクト(すなわちないなどと同じ階層に、共通して何も、異なる概念、中)です。
たとえば、Nodes
X、Y、Z座標を使用して、多くの幾何学的計算(おそらく点、幾何学的オブジェクトの一部)を実行できるシステムがあり、各ノードは自動エージェントであり、他のエージェントと通信できます。
おそらく、あなたはすでに2つのライブラリ、独自の名前空間を持つ各へのアクセス持っている(名前空間を使用する別の理由を...しかし、あなたは、あなたの名前空間をしていないでしょう?)、1ビーイングgeo
や他のビーイングをai
したがってown::Node
、ai::Agent
との両方から独自の派生物がありますgeo::Point
。
これは、代わりにコンポジションを使用すべきではないかどうかを自問する必要がある瞬間です。own::Node
が本当にaai::Agent
とaの両方である場合geo::Point
、合成は機能しません。
次にown::Node
、3D空間での位置に応じて他のエージェントと通信する、多重継承が必要になります。
(ai::Agent
そしてgeo::Point
、完全に、完全に、完全に無関係であることに気付くでしょう...これは多重継承の危険性を劇的に減らします)
他の場合があります:
this
)コンポジションを使用できる場合もあれば、MIの方が優れている場合もあります。重要なのは、あなたには選択肢があるということです。責任を持って実行してください(そしてコードをレビューしてもらいます)。
ほとんどの場合、私の経験では、違います。MIは、機能しているように見えても、適切なツールではありません。怠惰な人が、結果を認識せずに機能を積み上げるために使用できるためです(Car
anEngine
とaの両方を作成するなどWheel
)。
しかし時々、はい。そしてその時、MIほどうまくいくものはありません。
しかし、MIは臭いので、コードレビューでアーキテクチャを防御する準備をしてください(防御できない場合は防御すべきではないため、防御することは良いことです)。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加