「相続上の構図を優先」であるちょうど良いヒューリスティック
これは普遍的なルールではないため、コンテキストを考慮する必要があります。あなたが作曲をすることができるとき、決して継承を使用しないことを意味すると考えてはいけません。その場合は、継承を禁止することで修正できます。
この投稿に沿ってこの点を明らかにしたいと思います。
作曲のメリットを単独で擁護しようとはしません。私はトピックを検討します。代わりに、開発者がコンポジションを使用したほうがよい継承を検討する可能性がある状況について説明します。その点については、継承には独自のメリットがあります。
車両の例
私は物語の目的のために物事を愚かな方法でしようとする開発者について書いています
私たちはいくつかのOOPのコースが使用する古典例の変形のために行ってみよう...私たちは持っているVehicle
し、我々は派生クラスをCar
、Airplane
、Balloon
とShip
。
注:この例を基にする必要がある場合は、これらがビデオゲームのオブジェクトの一種であると考えてください。
そしてCar
、Airplane
それらは両方とも陸上で車輪の上を転がることができるので、いくつかの一般的なコードを持っているかもしれません。開発者は、そのための継承チェーンに中間クラスを作成することを検討できます。しかし、実際にはとの間にいくつかの共有コードもAirplane
ありBalloon
ます。そのために、継承チェーンに別の中間クラスを作成することを検討できます。
したがって、開発者は多重継承を探しています。開発者が多重継承を探している時点で、設計はすでに間違っています。
この動作をインターフェイスと構成としてモデル化することをお勧めします。そのため、複数のクラスの継承を実行せずに再利用できます。たとえば、開発者がFlyingVehicule
クラスを作成する場合。彼らはそれAirplane
がFlyingVehicule
(クラス継承)であると言っているでしょうが、代わりにそれAirplane
はFlying
コンポーネント(組成)を持ちAirplane
、IFlyingVehicule
(インターフェース継承)であると言うことができます。
必要に応じて、インターフェイスを使用して、(インターフェイスの)複数の継承を持つことができます。また、特定の実装に結合していません。コードの再利用性とテスト容易性の向上。
継承はポリモーフィズムのツールであることを忘れないでください。さらに、ポリモーフィズムは再利用性のためのツールです。コンポジションを使用してコードの再利用性を高めることができる場合は、そうしてください。合成がより良い再利用性を提供するかどうかわからない場合は、「継承よりも合成を優先する」が優れたヒューリスティックです。
言及せずにすべてのことAmphibious
。
実際、私たちは地面から落ちるものを必要としないかもしれません。Stephen Hurnの記事「継承よりもお気に入りの構成」のパート1とパート2に、より雄弁な例があります。
代替性とカプセル化
A
継承または構成する必要がありB
ますか?
のA
特殊化がリスコフ置換の原則をB
満たす必要がある場合、継承は実行可能であり、望ましいものですらあります。有効な代替ではない状況がある場合は、継承を使用しないでください。A
B
派生クラスを守るために、防衛的プログラミングの一形態としての合成に興味があるかもしれません。特に、いったんB
他の異なる目的で使用を開始すると、それらの目的により適したものに変更または拡張する圧力がかかる場合があります。B
無効な状態になる可能性のあるメソッドを公開するリスクがある場合A
、継承の代わりに構成を使用する必要があります。私たちがとの両方の作者であるとしてもB
、A
心配することは1つ少ないので、合成によりの再利用が容易になりB
ます。
必要のない機能がB
あるA
場合(A
現在の実装または将来のいずれかでそれらの機能が無効な状態になる可能性があるかどうかわからない場合)、コンポジションを使用することをお勧めします継承の代わりに。
合成には、実装の切り替えを許可し、モッキングを緩和するという利点もあります。
注:置換が有効であるにもかかわらず、合成を使用したい状況があります。インターフェイスまたは抽象クラス(別のトピックの場合に使用するもの)を使用して、その代替可能性をアーカイブし、実際の実装の依存性注入を使用した構成を使用します。
最後に、もちろん、継承は親クラスのカプセル化を破壊するため、親クラスを保護するために構成を使用する必要があるという議論があります。
継承はサブクラスをその親の実装の詳細に公開します。「継承はカプセル化を破壊する」とよく言われます
-デザインパターン:再利用可能なオブジェクト指向ソフトウェアの要素、Gang of Four
まあ、それは不十分に設計された親クラスです。それがあなたがすべき理由です:
継承を設計するか、禁止します。
-効果的なJava、Josh Bloch
ヨーヨー問題
合成が役立つ別のケースは、ヨーヨーの問題です。これはウィキペディアからの引用です:
ソフトウェア開発において、ヨーヨーの問題は、継承グラフが非常に長く複雑であるため、プログラマーが多くの異なるクラス定義間をたどり続ける必要があるプログラムをプログラマーが読み取って理解しなければならないときに発生するアンチパターンですプログラムの制御フロー。
たとえば、解決できます。C
クラスはclassから継承しませんB
。代わりに、クラスにC
はtypeのメンバーがあります。このメンバーは、type A
のオブジェクトである場合とそうでない場合がありますB
。この方法では、の実装の詳細に対してではなくB
、インターフェイスがA
提供するコントラクトに対してプログラミングします。
カウンターの例
多くのフレームワークは、構成よりも継承を優先します(これは、これまで議論してきたことの反対です)。開発者は、コンポジションで実装するとクライアントコードのサイズが大きくなるため、基本クラスに多くの作業を行うため、これを行う可能性があります。時々これは言語の制限が原因です。
たとえば、PHP ORMフレームワークは、マジックメソッドを使用して、オブジェクトに実際のプロパティがあるかのようにコードを記述できる基本クラスを作成できます。代わりに、魔法のメソッドで処理されるコードはデータベースに行き、特定の要求されたフィールドを照会し(おそらく将来の要求のためにキャッシュする)、それを返します。構成でそれを行うには、クライアントが各フィールドのプロパティを作成するか、マジックメソッドコードの何らかのバージョンを記述する必要があります。
補遺:ORMオブジェクトを拡張できる他の方法がいくつかあります。したがって、この場合、継承は必要ないと思います。もっと安い。
別の例では、ビデオゲームエンジンは、ターゲットプラットフォームに応じてネイティブコードを使用して3Dレンダリングとイベント処理を行う基本クラスを作成できます。このコードは複雑でプラットフォーム固有です。エンジンの開発者ユーザーがこのコードを処理することは高価でエラーが発生しやすくなります。実際、これはエンジンを使用する理由の一部です。
さらに、3Dレンダリングパーツがない場合、これはいくつのウィジェットフレームワークが機能するかを示します。これにより、OSメッセージの処理について心配する必要がなくなります。実際、多くの言語では、何らかの形式のネイティブビッディングなしでは、このようなコードを記述できません。さらに、それを行うと、移植性が低下します。代わりに、継承を使用して、開発者が互換性を(あまりにも)損なわない限り、将来サポートされる新しいプラットフォームにコードを簡単に移植できるようになります。
さらに、多くの場合、いくつかのメソッドをオーバーライドするだけで、他のすべてはデフォルトの実装のままにすることを考慮してください。コンポジションを使用していた場合、ラップされたオブジェクトに委任するだけの場合でも、これらすべてのメソッドを作成する必要がありました。
この議論により、構成が継承よりも保守性にとって最悪になる可能性がある点があります(基本クラスが複雑すぎる場合)。それでも、継承の保守性は構成の保守性よりも劣ることがあります(継承ツリーが複雑すぎる場合)。これはヨーヨーの問題で言及していることです。
提示された例では、開発者が他のプロジェクトで継承によって生成されたコードを再利用することはほとんどありません。これにより、構成ではなく継承を使用する再利用性が低下します。さらに、フレームワーク開発者は、継承を使用することにより、使いやすく、発見しやすいコードを多数提供できます。
最終的な考え
ご覧のとおり、コンポジションには、常にではなく、状況によっては継承よりもいくつかの利点があります。決定を下すには、コンテキストと関連するさまざまな要因(再利用性、保守性、テスト容易性など)を考慮することが重要です。最初のポイントに戻ります:「継承よりも構成を優先する」は、ちょうど良いヒューリスティックです。
また、私が説明する状況の多くは、TraitsまたはMixinsである程度解決できることにも気付くかもしれません。悲しいことに、これらは言語の優れたリストでは一般的な機能ではなく、通常はパフォーマンスコストがかかります。ありがたいことに、彼らの人気のあるいとこ拡張メソッドとデフォルト実装は、いくつかの状況を緩和します。
最近の投稿で、C#でUI、ビジネス、データアクセスの間にインターフェイスが必要な理由について、インターフェイスの利点について説明しています。分離を助け、再利用性とテスト容易性を容易にします。興味があるかもしれません。