web-dev-qa-db-ja.com

継承よりも合成を優先する

継承よりも合成を優先する

とても人気のあるフレーズです。私はいくつかの記事を読んで、最後に各記事が言っています

クラス間に純粋なIS-A関係がある場合、継承を使用します。

この記事 の例:

ここで、AppleFruitの間には、明確なIS-A関係、つまり_があります。Apple IS-A Fruitですが、著者はApple HAS-A Fruit(composition)としても示し、継承を実装した場合の落とし穴を示しています。

私はここで、ステートメントの意味が何であるかと少し混乱しました

クラス間に純粋なIS-A関係がある場合、継承を使用します。

継承を介して構成を使用することは、常にtryが構成を適用することを意味するかif if純粋なIS-A関係があり、構成が意味をなさない場合にのみ継承を残します

53
a Learner

メソッドをオーバーライドして別の多態的な動作を定義するのではなく、継承を使用してスーパークラスのコードを再利用する場合、多くの場合、継承の代わりに構成を使用する必要があります。

Java.util.Propertiesクラスは、継承の不適切な使用の良い例です。 singプロパティを保存するためのHashtableではなく、extends Hashtable。メソッドを再利用し、委任を使用してそれらの一部を再実装しないようにします。

73
JB Nizet

これは、オブジェクト指向設計で最も議論されているポイントの1つだと思います。記事で提案されているように、継承よりも構成が常に優先されます。それは、決して継承を使用すべきではないという意味ではありません。あなたはそれがより理にかなっているはずです(これは議論の余地があります)。

コンポジションを使用することには多くの利点がありますが、そのうちのいくつかは次のとおりです。

  • 実装を完全に制御できます。つまり、公開する予定のメソッドのみを公開できます。
  • スーパークラスの変更は、クラス内でのみ変更することで保護できます。クラスを使用するクライアントクラスは、変更する必要がありません。
  • スーパークラスを読み込むタイミングを制御できます(遅延読み込み)
6
Chandra

良いガイドラインは次のようになると思います。

IS-A関係がある場合は、継承を使用します。それ以外の場合は、構成を使用します。

この理由は、オブジェクト指向設計の別の概念であるポリモーフィズムに関連しています。多態性は、オブジェクトを別のオブジェクトの代わりに使用できる多くのOOP言語の機能です。ただし、最初のクラスが2番目のサブクラスである場合に限ります。

説明するために、動物のタイプを取り込む機能があるかどうかを想像してください。継承を使用する場合、1つの関数しかありません。

void feed( Animal a );

多型性は、私たちが入れた動物のサブクラスが受け入れられることを保証します。それ以外の場合は、タイプごとに1つの関数を作成する必要があります。この利点は、欠点(カプセル化の削減など)を上回ると思います。

IS-A関係がなければ、ポリモーフィズムはあまり効果的ではないと思います。したがって、構成を使用し、クラス間のカプセル化/分離を強化することをお勧めします。

4
bancsy

記事では、そのようなフレーズはありません。

use inheritance when there is pure IS-A relationship between classes

さらに、Googleはあなたの投稿以外の場所でそれを見つけませんでした。

代わりに、記事には次のように記載されています。

Make sure inheritance models the is-a relationship. My main guiding philosophy is that inheritance should be used only when a subclass is-a superclass. In the example above, an Apple likely is-a Fruit, so I would be inclined to use inheritance.

これは、IS_A関係がある場合は、構成ではなく、最初に継承を使用することを意味します。また、using composition over inheritanceの設定はありません-それぞれが独自の役割に適しています。

3

また、 Decorator pattern は、継承よりも合成を使用してオブジェクトに責任を動的に追加できる例です。デコレータパターンの例については、「継承よりも合成を優先」という項目の「有効なJava」に記載されています。 Java AP​​Iから例を挙げるには、 BufferedReaderは(FileReader、PipedReader、FilterReaderなどを拡張する代わりに)Readerを装飾するため、あらゆる種類のReaderを装飾できます。バッファリング機能は、実行時にこれらのリーダーに添付されます。これは、デコレータパターンです。

ここでは、サブクラス化による拡張は実用的ではありません。

3
user1168577

関係が永続的な場合、継承を使用します。無料の動作を得るためだけに拡張機能を使用しないでください。これは、彼らがIS-Aの例で言及していることです。拡張クラスが本当に親の拡張である場合にのみ拡張します。一般的には、カットして乾燥させない場合があります。 「正しい」クラスから拡張する必要がある場合、構成は将来的にも柔軟性を提供します。

これは古いものですが、拡張に関する素晴らしい記事です。

http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html

2
tjg184

私はノーと言うでしょう。フルーツ/アップルのシナリオでは、継承を使用することは理にかなっています。記事の著者も次のことを確認しています。「上記の例では、Apple is-a Fruitであるため、継承を使用する傾向があります。」.

サブクラスが明らかにスーパークラスである場合、継承は問題ありません。ただし、実際には、コンポジションを使用することで解決できる状況がさらに多く見つかります。

0
Tom