この「継承よりも有利な構成」という概念はどこから来たのでしょうか?


143

過去数か月で、「継承よりも好意的な構成」というマントラがどこからともなく生まれ、プログラミングコミュニティ内でほぼ何らかのミームになったようです。そして、それを見るたびに、私は少し神秘的になります。誰かが「ハンマーよりもドリルを好む」と言ったようです。私の経験では、構成と継承は異なるユースケースを持つ2つの異なるツールであり、それらを互換性があり、一方が本質的に他方より優れているかのように扱うことは意味がありません。

また、なぜ継承が悪く、構成が良いのについての本当の説明は見ていません。信仰によって受け入れられるだけなのでしょうか?リスコフ置換とポリモーフィズムには、明確で明確な利点があり、IMOはオブジェクト指向プログラミングを使用するすべてのポイントを構成します。構成を優先して破棄する理由を説明する人はいません。

この概念がどこから来たのか、そしてその背後にある理論的根拠は誰か知っていますか?


45
他の人が指摘しているように、それはかなり前からあります-あなたが今それを聞いていることに驚いています。これは、Javaのような言語で大規模なシステムを長期間構築している人にとって直感的なものです。これは私がこれまでに行ったインタビューの核であり、候補者が相続について話し始めると、私は彼らのスキルレベルと経験量を疑い始めます。ここで継承が脆い溶液(多くの多くの人がある)である理由の入門です:artima.com/lejava/articles/designprinciples4.html
Janx

6
@Janx:たぶんそれだけです。Javaのような言語で大きなシステムを構築することはありません。Delphiでそれらを構築しますが、Liskovの置換とポリモーフィズムがなければ、何もできません。そのオブジェクトモデルは、JavaやC ++とは特定の点で異なり、この格言が解決することを意図していると思われる問題の多くは、Delphiには実際には存在しないか、それほど問題ではありません。異なる視点からの異なる視点、私は推測する。
メイソンウィーラー

14
Delphiで比較的大規模なシステムを構築するチームに数年費やし、確かに高い継承ツリーがチームに噛みつき、大きな痛みを引き起こしました。Delphiの使用ではなく、SOLID原則への注意が問題領域の回避に役立つと思われます。
ベヴァン

8
過去数ヶ月?!?
ジャス

6
私見では、その概念は、インターフェイスの継承(つまり、純粋なインターフェイスを使用したサブタイピング)と実装の継承の両方をサポートするさまざまな言語に完全に調整されたことはありません。多くの人がこのマントラに従い、十分なインターフェースを使用していません。
ウリ

回答:


136

GoFよりもずっと前に、構成と継承の議論を聞いたことがあると思いますが、特定のソースに指を当てることはできません。とにかくBoochだったかもしれません。

<暴言>

ああ、しかし多くのマントラのように、これは典型的な線に沿って退化しています:

  1. キャッチフレーズを元の複雑な議論を思い出させるものとして作り出した、尊敬される情報源による詳細な説明と議論とともに紹介されています。
  2. 一般的にn00bの間違いについてコメントするとき、それはしばらくの間、いくつかの知人が知っているクラブのウィンクと共有されます
  3. すぐにそれは説明を読んだことのない数千人によって無意識に繰り返されますが考えない言い訳として、そして他の人よりも優れていると感じる安価で簡単な方法としてそれを使用するの大好きです
  4. 最終的に、合理的な暴言の量は「ミーム」の潮流を食い止めることができません-そして、パラダイムは宗教と教義に縮退します。

元々はn00bsを悟りに導くことを意図していたミームは、今では無意識にそれらをudge打するクラブとして使用されています。

構成と継承は非常に異なるものであり、互いに混同しないでください。構成を使用して継承をシミュレートするために多くの余分な作業を行うことができるのは事実ですが、これは継承を二流市民にしたり、構成を好きな息子にしたりしません。多くのn00bsがショートカットを継承として使用しようとするという事実はメカニズムを無効にするものではなく、ほとんどすべてのn00bsが間違いから学び、それによって改善されます。

してくださいTHINKあなたのデザインについて、およびスローガンを吐出を停止。

</ rant>


16
Boochは、実装の継承がクラス間の高い結合を導入すると主張します。一方、彼は継承を、OOPと手続き型プログラミングを区別する重要な概念と考えています。
ネマンジャトリフノヴィッチ

13
この種のことには言葉が必要です。早すぎる逆流、多分?
ヨルグWミットタグ

8
@Jörg:早漏のような用語に何が起こるか知っていますか?正確に上記で説明されていること。:)(ところで逆流は早すぎることはありませんか?)
Bjarke Freund-Hansen

6
@Nemanja:その通り。問題は、この結合が本当に悪いかどうかです。クラスが概念的に強く結合され、概念的にスーパータイプとサブタイプの関係を形成し、言語レベルで正式に分離されていても概念的に分離できない場合、強い結合は問題ありません。
-dsimcha

5
マントラは単純です。「プレイドーを形にできるからといって、すべてをプレイドーから作るべきだというわけではありません」。;)
エヴァンプライス

73

経験。

あなたが言うように、彼らはさまざまな仕事のためのツールですが、フレーズは人々がそのように使用していないために出てきました。

継承は主にポリモーフィックなツールですが、後の危険にさらされている一部の人々は、コードを再利用/共有する方法としてそれを使用しようとします。「継承すれば、すべてのメソッドを無料で取得できる」という理論的根拠がありますが、これらの2つのクラスには潜在的に多相関係がないという事実を無視しています。

それで、なぜ継承よりも合成を好むのか-単にクラス間の関係が多態的な関係ではないことが多いからです。それは、継承によってひざまずく反応をしないように人々に思い出させるのを助けるためだけに存在します。


7
したがって、基本的には、「リスコフ置換を理解していない場合、最初にOOPを使用するべきではありません」とは言えません。コーダー?
メイソンウィーラー

13
@Mason:他のマントラと同様に、「継承よりも好み」は初心者プログラマーを対象としています。継承を使用するタイミングと構成を使用するタイミングをすでに知っている場合、そのようなマントラを繰り返すのは無意味です。
ディーンハーディング

7
@Dean- 初心者が継承のベストプラクティスを理解していない人と同じであるかどうかはわかりません。それ以上のことがあると思います。劣悪な継承は、私の仕事における多くの頭痛の原因であり、「初心者」と見なされるプログラマーによって書かれたコードではありません。
ニコール

6
@Renesis:私がそれを聞いたときに最初に思うことは、「10年の経験を持っている人もいれば、1年の経験を10回繰り返している人もいる」ということです。
メイソンウィーラー

8
OOを最初に "Getting"した直後にかなり大きなプロジェクトを設計し、それに大きく依存していました。それはうまくいきましたが、私は絶えずデザインが脆いことを発見しました-軽度の刺激をあちこちで引き起こし、時には特定のリファクタリングを完全な雌犬にしました。全体の経験を説明するのは一種の困難ですが、「相続よりも賛成」というフレーズは正確に説明しています。「継承を避ける」とは言わず、暗示するものでもないことに注意してください。選択が明らかでない場合は、少しだけ微調整するだけです。
ビルK

43

これは新しいアイデアではなく、1994年に発行されたGoFデザインパターンの本で実際に紹介されたと思います。

継承の主な問題は、それがホワイトボックスであることです。定義により、継承元のクラスの実装の詳細を知る必要があります。一方、コンポジションでは、作成しているクラスのパブリックインターフェイスのみが重要です。

GoFブックから:

継承は親の実装の詳細にサブクラスを公開し、「継承はカプセル化を中断する」とよく言われます

GoF本に関するウィキペディアの記事には、まともな紹介があります。


14
私はそれに同意しません。継承元のクラスの実装の詳細を知る必要はありません。クラスによって公開れるパブリックおよび保護されたメンバーのみ。実装の詳細を知る必要がある場合、あなたまたは基本クラスを書いた人のいずれかが何か間違ったことをしており、欠陥が基本クラスにある場合、構成はそれを修正/回避するのに役立ちません。
メイソンウィーラー

18
読んでいないものにどのように反対できますか?GoFからの堅実なページと議論の半分がありますが、これはほんの小さなビューです。
哲学者

7
@pholosodad:読んだことのないものには同意しません。私が読んだ「定義から、継承元のクラスの実装の詳細を知る必要がある」とDeanが書いたことに同意しません。
メイソンウィーラー

10
私が書いたのは、GoFブックに記載されている内容の要約です。私はそれを少し強く表現したかもしれませんが(すべての実装の詳細を知る必要はありません)、それがGoFが継承よりも合成を優先すると言う一般的な理由です。
ディーンハーディング

2
私が間違っている場合は修正しますが、「暗黙的な(継承)よりも明示的なクラス関係(つまりインターフェイス)を優先する」と考えています。前者は、その方法を説明せずに、必要なものを説明します。後者は、その方法を教えてくれるだけでなく、後悔するようになります。
エヴァンプライス

26

あなたの質問の一部に答えるために、このコンセプトは、1994年に最初に出版された「GOF Design Patterns:Elements of Reusable Object-Oriented Software」ブックに最初に登場したと思います。

継承よりもオブジェクト構成を優先する

彼らはこの声明の前に、継承と構成を簡単に比較します。彼らは「決して継承を使用しない」とは言いません。


23

「継承を介した構成」は、「クラスのデータ(または動作)を別のクラスに組み込む必要があると感じたとき、継承を盲目的に適用する前に常に構成を検討する」という短い(そして明らかに誤解を招く)方法です。

なぜこれが本当ですか?継承により、2つのクラス間に厳密なコンパイル時の結合が作成されるためです。対照的に構成は疎結合であり、とりわけ、懸念の明確な分離、実行時の依存関係の切り替えの可能性、より簡単でより独立した依存関係のテスト可能性を可能にします。

それは、それが有用ではないということではなく、コストがかかるため、継承を慎重に処理する必要があることを意味します。実際には、合成された依存関係を具体的なサブクラス自体ではなく、抽象スーパークラスにしたいことが多いため、「継承に対する構成」はしばしば「構成+継承に対する継承」になります。これにより、実行時に依存関係の具体的な実装を切り替えることができます。

そのため(特に)、おそらく、継承は通常の継承よりもインターフェイス実装または抽象クラスの形式で使用されることが多くなります。

(比)的な)例は次のとおりです。

「Snakeクラスがあり、そのクラスの一部としてSnakeが噛んだときに何が起こるかを含めたいと思います。Bite()メソッドを持つBiterAnimalクラスをSnakeに継承させ、そのメソッドをオーバーライドして有毒なバイトを反映させたいと思います。 。しかし、Inheritance上のCompositionは、代わりにCompositionを使用するように警告します...私の場合、これは、Biteメンバーを持つSnakeに変換できます。Biteクラスは、いくつかのサブクラスを持つ抽象(またはインターフェース)になります。 VenomousBiteとDryBiteのサブクラスを持ち、ヘビが成長するにつれて同じSnakeインスタンスで噛み付きを変えることができるようになりました。さらに、Biteのすべてのエフェクトを個別のクラスで処理すると、Frostクラスで再利用できます、霜は噛むがBiterAnimalではない、などなど...」


4
ヘビの非常に良い例。最近、私自身のクラスで同様のケースに出会いました
-Maksee

22

構成のためのいくつかの可能な引数:

構成は言語/フレームワークにとらわれない
継承であり、サブ/スーパークラスがアクセスできるものや、仮想メソッドなどのパフォーマンスへの影響などに関して、言語によって実施/要求/有効化が異なります。構成は非常に基本的で必要です言語サポートがほとんどないため、異なるプラットフォーム/フレームワークでの実装は、構成パターンをより簡単に共有できます。

合成はオブジェクトを構築する非常にシンプルで触覚的な方法です
継承は比較的簡単に理解できますが、実生活ではそれほど簡単に実証できません。実生活の多くのオブジェクトは、パーツに分解して構成できます。自転車は、2つの車輪、フレーム、シート、チェーンなどを使用して構築できるとします。構成によって簡単に説明できます。一方、継承のメタファーでは、自転車は一輪車を拡張していると言うことができますが、ある程度実現可能ですが、構成よりも実際の画像からはるかに遠く離れています(明らかにこれはあまり良い継承の例ではありませんが、ポイントは同じままです)。単語の継承(少なくとも私が期待するほとんどのアメリカ英語話者)でさえ、「故人の親fromから受け継がれたもの」に沿って意味を自動的に呼び出します。

構成はほとんど常に柔軟
です構成を使用すると、いつでも独自の動作を定義するか、構成部分のその部分を公開するかを選択できます。このようにして、継承階層によって課される制限(仮想と非仮想など)に直面することはありません。

そのため、コンポジションは自然に、継承よりも理論的制約の少ない単純なメタファーであるためです。さらに、これらの特定の理由は、設計時により明らかになる場合があります。または、継承のいくつかの問題点を扱う場合に突き出る可能性があります。

免責事項:
明らかにその明確なカット/一方通行ではありません。各デザインは、いくつかのパターン/ツールの評価に値します。継承は広く使用されており、多くの利点があり、多くの場合、構成よりもエレガントです。これらは、作曲を好むときに使用できるいくつかの考えられる理由です。


1
「明らかに、これほど明確ではない/片道です。」構成が継承より柔軟ではない(または同等に)場合 私はそれが非常に多くのことを主張である一方通行。継承は、特別な構成の場合の単なる構文糖です。
weberc2

明示的に実装されたインターフェイスを使用してコンポジションを実装する言語を使用する場合、コンポジションは多くの場合柔軟性がありません。#jmtcw
MikeSchinkel

11

おそらく、ここ数か月でこれを言っている人がいることに気づいたかもしれませんが、それよりもずっと長い間、優れたプログラマーに知られています。私は確かに、適切な場所で約10年間それを言ってきました。

概念のポイントは、継承に対する概念上のオーバーヘッドが大きいことです。継承を使用している場合、すべてのメソッド呼び出しには暗黙的なディスパッチが含まれます。深い継承ツリー、複数のディスパッチ、または(さらに悪いことに)両方がある場合、特定の呼び出しで特定のメソッドがディスパッチされる場所を特定することは、ロイヤルPITAになります。これにより、コードに関する正しい推論がより複雑になり、デバッグが難しくなります。

説明のために簡単な例を挙げましょう。継承ツリーの奥深く、誰かがmethodという名前を付けたとしfooます。その後、誰かがやって来てfoo、ツリーの一番上に追加しますが、何か違うことをします。(このケースは、多重継承でより一般的です。)ルートクラスで働いている人は、あいまいな子クラスを壊してしまい、おそらく気付かないでしょう。最上位の人は子クラスをテストすることを考えず、子クラスのテストは最上部で作成された新しいメソッドをテストすることを考えないため、ユニットテストで100%のカバレッジがあり、この破損に気付かないことがあります。 。(確かに、これをキャッチする単体テストを作成する方法はありますが、そのようにテストを簡単に作成できない場合もあります。)

対照的に、コンポジションを使用する場合、通常、各呼び出しで、呼び出しのディスパッチ先が明確になります。(OK、たとえば依存関係の挿入など、制御の反転を使用している場合、呼び出しがどこに行くのかを把握することも問題になります。しかし、通常は把握する方が簡単です。)これにより、推論が容易になります。おまけとして、合成はメソッドを互いに分離させることになります。上の例は、子クラスが不明瞭なコンポーネントに移動するため、そこで発生することはありません。また、への呼び出しfooが不明瞭なコンポーネントまたはメインオブジェクトのどちらを対象としていたのかについては疑問がありません。

継承と構成は、2つの異なるタイプのサービスを提供する非常に異なる2つのツールであることは間違いありません。確かに継承は概念的なオーバーヘッドをもたらしますが、それがジョブに適切なツールである場合、それを使用しないで手作業で行うよりも概念的なオーバーヘッドが少なくなります。彼らが何をしているのかを知っている人は誰も、決して継承を使うべきではないとは言いません。しかし、それが正しいことであることを確認してください。

残念ながら、多くの開発者はオブジェクト指向ソフトウェアについて学び、継承について学び、それからできるだけ頻繁に新しいaを使いに行きます。つまり、構成が適切なツールである場合に、継承を使用しようとします。うまくいけば、彼らは時間内に良くなることを願っていますが、多くの場合、これは、いくつかの手足を取り除いた後などに起こりません。


3
よし、C ++の観点からは理にかなっていると思います。それはDelphiの問題ではないので、私は考えもしなかったことです。(多重継承はありません。ベースクラスにメソッドがあり、派生メソッドに同じ名前の別のメソッドがあり、ベースメソッドをオーバーライドしない場合、コンパイラは警告を発行するため、誤って終了することはありませんこの種の問題で)。
メイソンウィーラー

1
@Mason:Object Pascal(別名Delphi)のAnderのバージョンは、壊れやすい基本クラスの継承の問題に関しては、C ++およびJavaよりも優れています。C ++やJavaとは異なり、継承された仮想メソッドのオーバーロードは暗黙的ではありません。
ビットトゥイドラ

2
@ bit-twiddler、C ++とJavaについてあなたが言うことは、Smalltalk、Perl、Python、Ruby、JavaScript、Common Lisp、Objective-C、およびあらゆる種類のオブジェクト指向サポートを提供する私が学んだ他のすべての言語について言えます。そうは言っても、グーグルはC#がObject Pascalのリードに従うことを示唆しています。
-btilly

3
これは、Anders HejlsbergがBorlandのObject Pascal(別名Delphi)とC#のフレーバーを設計したためです。
ビットツイダー

8

オブジェクト指向の初心者は、必要のないときに継承を使用する傾向があるという観察に対する反応です。継承は確かに悪いことではありませんが、使い古される可能性があります。あるクラスが別のクラスの機能のみを必要とする場合、おそらく構成は機能します。他の状況では、継承は機能しますが、構成は機能しません。

クラスから継承することは多くのことを意味します。Derivedはベースの一種であることを意味します(詳細についてはLiskov Substitutionの原則を参照)。ベースを使用するときは常にDerivedを使用するのが理にかなっています。Baseの保護されたメンバーおよびメンバー関数への派生アクセスを提供します。これは密接な関係です。つまり、高いカップリングを持ち、一方を変更するには他方を変更する必要があります。

カップリングは悪いことです。プログラムの理解と修正が難しくなります。他の条件が同じであれば、常に結合の少ないオプションを選択する必要があります。

したがって、構成または継承のいずれかが効果的に機能する場合は、結合が低いため、構成を選択します。構成が効果的に仕事をせず、継承がする場合、継承を選択する必要があります。


「他の状況では、継承は機能しますが、構成は機能しません。」いつ?継承は、コンポジションとインターフェイスのポリモーフィズムを使用してモデル化できるため、コンポジションが機能しない状況がある場合、私は驚かれることでしょう。
weberc2

8

ここに私の2セントがあります(すでに挙げられているすべての優れたポイントを超えています):

私見、それはほとんどのプログラマーが実際に継承を得ないという事実に帰着し、そしてこの概念がどのように教えられているかの結果として、それを無理やり終わることになる。この概念は、彼らに正しいやり方を教えることに集中するのではなく、彼らにそれをやりすぎないように説得する方法として存在します。

教えるかメンタリングに時間を費やした人は誰でも、これが頻繁に起こること、特に他のパラダイムの経験がある新しい開発者であるということを知っています:

これらの開発者は当初、継承はこの恐ろしい概念であると感じています。そのため、インターフェイスのオーバーラップ(たとえば、共通のサブタイピングのない同じ指定された動作)と、共通の機能を実装するためのグローバルを使用して、タイプを作成します。

その後(多くの場合、熱心な教育の結果として)、彼らは継承の利点について学びますが、それはしばしば再利用するための包括的なソリューションとして教えられます。共有された動作は継承を通じて共有されなければならないという認識に終わります。これは、サブタイプではなく実装の継承に重点が置かれることが多いためです。

ケースの80%で十分です。しかし、残りの20%は問題が始まる場所です。書き換えを回避し、実装の共有を活用したことを確認するために、基になる抽象化ではなく、目的の実装を中心に階層の設計を開始します。「スタックが二重にリンクされたリストから継承する」は、この良い例です。

ほとんどの教師は、この時点でインターフェイスの概念を導入することを主張しません。それは別の概念であるため、または(C ++では)この段階では教えられない抽象クラスと多重継承を使用してそれらを偽造する必要があるためです Javaでは、多くの教師は「多重継承なし」または「多重継承は悪」とインターフェースの重要性を区別しません。

これはすべて、実装継承で余分なコードを書く必要がないという美しさの多くを学んだという事実によってさらに複雑になり、大量の単純な委任コードは不自然に見えます。そのため、構成は複雑に見えるため、これらの経験則を使用して、新しいプログラマーにとにかく使用してもらう必要があります(これもやりすぎです)。


それが教育の本当の部分です。あなたは20%の残りを取得するために、より高いコースを行う必要がありますが、あなたは、学生として、基本的な、そしておそらく中間コースを教えられました。エンディングでは、コースをうまくやったので、よく教えられていると感じます(そして、単にそれをはしごのピンだけで見ないでください)。これは現実の一部に過ぎず、コーダーを攻撃するのではなく、副作用であることを理解することが最善策です。
独立

8

コメントの1つで、メイソンは、いつか有害と考えられる継承について話すと述べました。

そうだといい。

継承の問題は一度に簡単で、致命的であり、1つの概念が1つの機能を持つべきであるという考えを尊重しません。

ほとんどのオブジェクト指向言語では、基本クラスから継承する場合、次のことを行います。

  • インターフェイスから継承
  • その実装から継承します(データとメソッドの両方)

そして、ここが問題になります。

これが唯一のアプローチではありませんが、00言語はほとんどこれに固執しています。幸いなことに、それらにはインターフェース/抽象クラスが存在します。

また、そうすることの容易さの欠如は、それを広く使用することに貢献します:率直に言って、これを知っていても、インターフェイスから継承し、構成によって基本クラスを埋め込み、ほとんどのメソッド呼び出しを委任しますか?ただし、インターフェイスに突然新しいメソッドが表示され、それを実装する方法を意識的に選択する必要がある場合は、警告されることもあります。

カウンターポイントとして、Haskell純粋なインターフェース(概念と呼ばれる)から「派生」する場合にのみ、リスコフの原理を使用することを許可してください(1)。既存のクラスから派生することはできません。コンポジションのみがデータを埋め込むことができます。

(1)概念は実装に適切なデフォルトを提供する場合がありますが、データがないため、このデフォルトは、コンセプトが提案する他の方法または定数の観点からのみ定義できます。


7

簡単な答えは次のとおりです。継承は構成よりも結合が大きい。他の点では同等の品質の2つのオプションが与えられた場合、結合の少ないオプションを選択します。


2
しかし、それが全体のポイントです。それらは「他の点では同等」ではありません。継承により、OOPを使用する全体のポイントであるリスコフ置換とポリモーフィズムが可能になります。構成はしません。
メイソンウィーラー

4
多態性/ LSPはOOPの「全体」ではなく、1つの機能です。カプセル化、抽象化などもあります。継承は「ある」関係を意味し、集約は「ある」関係をモデル化します。
スティーブ

@Steve:データ構造とプロシージャ/関数の作成をサポートする任意のパラダイムで抽象化とカプセル化を行うことができます。しかし、ポリモーフィズム(特にこのコンテキストでの仮想メソッドのディスパッチを指す)とLiskov置換は、オブジェクト指向プログラミングに固有のものです。それが私が意味したことです。
メイソンウィーラー

3
@Mason:ガイドラインは「継承よりも構成を優先する」ことです。これは、継承を使用しない、または回避さえしないことを意味するものではありません。それは、他のすべてが等しいとき、構成を選択することをすべて言っています。ただし、継承が必要な場合は使用してください!
ジェフリーファウスト

7

この種のアドバイスは、「飛行するよりも運転することを好む」というようなものだと思います。つまり、飛行機には車に勝るあらゆる種類の利点がありますが、それにはある程度の複雑さが伴います。そのため、多くの人が都心から郊外に飛ぶ場合、実際に聞く必要があるのは、飛ぶ必要はないということです。実際、飛ぶことで長期的にはより複雑になります。短期的にはクール/効率的/簡単に見える場合でも。あなた飛ぶ必要があるとき、それは一般的に明らかであると思われます。

同様に、継承は構成ではできないことを行うことができますが、必要でないときではなく、必要なときにそれを使用する必要があります。したがって、継承が必要でない場合に継承が必要であると思わない場合、「構成を優先する」というアドバイスは必要ありません。しかし、多くの人はそうし、そのアドバイスを必要とます。

継承が本当に必要な場合、それは明白であり、それを使用する必要があることは暗黙のうちに想定されています。

また、スティーブン・ロウの答え。本当に、本当に。


1
あなたのアナロジーが好きです
バリルロイド

2

継承は本質的に悪いわけではなく、構成は本質的に良いというわけではありません。これらは、OOプログラマーがソフトウェアの設計に使用できる単なるツールです。

クラスを見ると、絶対に必要な以上のことを実行していますか(SRP)?機能を不必要に複製する(DRY)か、または他のクラスのプロパティやメソッドに過度に関心がある(Feature Envy)クラスは、これらの概念(そしておそらくそれ以上)の全てに違反している場合、それはしようとしている神クラス。これらは、ソフトウェアの設計時に発生する可能性のある多くの問題であり、必ずしも継承の問題ではありませんが、ポリモーフィズムが適用されている場合、大きな頭痛や脆弱な依存関係をすぐに引き起こす可能性があります。

問題の原因は、継承についての理解不足、および設計上の選択の悪さ、またはおそらく単一責任原則に準拠していないクラスに関連する「コードのにおい」を認識しないことです。 ポリモーフィズムリスコフ置換は、合成のために破棄する必要はありません。ポリモーフィズム自体は、継承に依存せずに適用できます。これらはすべて非常に相補的な概念です。思慮深く適用された場合。秘Theは、コードをシンプルかつクリーンに保ち、堅牢なシステム設計を作成するために作成する必要のあるクラスの数を過度に気にしないことです。

継承よりも合成を優先するという問題に関しては、これは実際に、解決される問題に関して最も意味のある設計要素の思慮深い適用の別のケースです。振る舞いを継承する必要がない場合、おそらくコンポジションが非互換性と後の主要なリファクタリングの努力を避けるのに役立つので、おそらくそうすべきではありません。一方、重複がすべて同じクラスのグループに集中するように多くのコードを繰り返していることがわかった場合、共通の祖先を作成すると同一の呼び出しとクラスの数を減らすのに役立つ可能性があります各クラス間で繰り返す必要がある場合があります。したがって、あなたは作曲を支持していますが、継承が決して適用可能であるとは想定していません。


-1

実際の引用は、ジョシュア・ブロッホの「効果的なジャワ」から来ていると思います。そこでは章の一つのタイトルです。彼は、継承はパッケージ内で、クラスが拡張用に特別に設計および文書化されている場合は安全であると主張しています。他の人が指摘したように、サブクラスはそのスーパークラスの実装の詳細に依存するため、継承がカプセル化を破ると主張します。これは脆弱性につながる、と彼は言います。

彼はそれについて言及していませんが、Javaの単一継承は、合成が継承よりもはるかに柔軟性を与えることを意味するように思えます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.