継承と構成の違い


208

構成と継承は同じですか?構成パターンを実装したい場合、Javaでそれを行うにはどうすればよいですか?


2
他の関連する質問:継承が達成できない構成が何かありますか?stackoverflow.com/questions/2238642/…–
ewernli


1
この記事では、私には有用であったので、助け: thoughtworks.com/insights/blog/...
qmasterの

回答:


321

それらは完全に異なります。継承は「is-a」関係です。作曲は「はさみ」です。

Cを拡張するのではなく、クラスのフィールドとして別のクラスのインスタンスを持つことによって、構成を行いますC。構成が継承よりもはるかに優れていた良い例はjava.util.Stack、現在拡張されていjava.util.Vectorます。これは失敗と見なされます。スタックの「is-NOT-a」ベクトル。要素を勝手に挿入したり削除したりしてはいけません。代わりに作曲しているはずです。

残念ながら、継承階層を変更すると既存のコードとの互換性が失われるため、この設計ミスを修正するのは遅すぎます。持っていたStack使用される組成物の代わりに継承し、常にAPIに違反することなく、他のデータ構造を使用するように変更することができます

Josh Blochの著書「Effective Java 2nd Edition」を強くお勧めします

  • 項目16:継承よりも構成を優先する
  • 項目17:継承のための設計と文書化、または継承の禁止

優れたオブジェクト指向設計は、既存のクラスを自由に拡張することではありません。あなたの最初の本能は、代わりに作曲することです。


以下も参照してください。


4
面白い。構成を使用する新しいクラスjava.util.Stack2を作成しないのはなぜですか?
2014年

5
この回答に感謝します。しかし、私は、答えが軌道から外れ、言語(および特定のパッケージ)の設計を取り巻く懸念について、構成と継承について尋ねられた質問に答えるよりも深く掘り下げているように感じます。私は、リソースを引用してSOに関する質問に答えるのがとても好きです。1行の合計よりも詳細な要約を提供することなく外部リソースにリンクしないでください。
Thomas

5
悪い例です。VectorとStackが何であるかを理解するために、追加の検索を行わなければなりませんでした。
Sam Ramezanli 2017年

この記事で述べたように、この継承は構成より継承を選択することについての誤った
blog

212

構成はHAS A
継承を意味するIS A

Example:車にはエンジンがあり、車自動車です

プログラミングでは、これは次のように表されます。

class Engine {} // The Engine class.

class Automobile {} // Automobile class which is parent to Car class.

class Car extends Automobile { // Car is an Automobile, so Car class extends Automobile class.
  private Engine engine; // Car has an Engine so, Car class has an instance of Engine class as its member.
}

7
多くの解釈で車と自動車は同等なので、「自動車」を「動力車」に変更します。
nanofarad 2013年

5
@hexafraction私はビークルがより良い選択である可能性が高いことに同意しますが、同時に-codaddict-は質問されたものに対して問題なくポイントを示しました。
nckbrz 14年

5
「エンジン」:-/
Omar Tariq

うまくいきました。+1。さらに読み取りjavarevisited.blogspot.in/2015/06/...
roottraveller

@AndreyAkhmetov Automobile typeはTypeのフィールドを持つことができますEnum
Ojonugwa Jude Ochalifu

42

継承はどのように危険なのでしょうか?

例を見てみましょう

public class X{    
   public void do(){    
   }    
}    
Public Class Y extends X{
   public void work(){    
       do();    
   }
}

1)上記のコードで明らかなように、クラスYはクラスXと非常に強い結合を持っています。スーパークラスXで何かが変更されると、Yは劇的に壊れる可能性があります。将来のクラスXが以下のシグネチャで機能するメソッドを実装すると仮定

public int work(){
}

変更はクラスXで行われますが、クラスYをコンパイルできなくなります。したがって、この種の依存関係はあらゆるレベルに及ぶ可能性があり、非常に危険な場合があります。スーパークラスのすべてのサブクラス内のコードに対する完全な可視性が常にない場合があり、サブクラスは常にスーパークラスで何が起こっているのかを認識し続ける場合があります。したがって、この強力で不要な結合を回避する必要があります。

コンポジションはこの問題をどのように解決しますか?

同じ例を修正して見てみましょう

public class X{
    public void do(){
    }
}

Public Class Y{
    X x = new X();    
    public void work(){    
        x.do();
    }
}

ここでは、YクラスにXクラスの参照を作成し、Xクラスのインスタンスを作成してXクラスのメソッドを呼び出しています。これで、その強い結合はすべてなくなりました。スーパークラスとサブクラスは、現在非常に独立しています。クラスは継承状況で危険だった変更を自由に行うことができます。

2)構成の2番目の非常に優れた利点は、メソッド呼び出しの柔軟性を提供することです。次に例を示します。

class X implements R
{}
class Y implements R
{}

public class Test{    
    R r;    
}

r参照を使用するTestクラスでは、XクラスとYクラスのメソッドを呼び出すことができます。この柔軟性は継承にありませんでした

3)別の大きな利点:単体テスト

public class X {
    public void do(){
    }
}

Public Class Y {
    X x = new X();    
    public void work(){    
        x.do();    
    }    
}

上記の例では、xインスタンスの状態が不明な場合、いくつかのテストデータを使用して簡単にモックアップでき、すべてのメソッドを簡単にテストできます。インスタンスの状態を取得してメソッドを実行するためにスーパークラスに大きく依存していたため、継承ではこれはまったく不可能でした。

4)継承を避けるべきもう1つの理由は、Javaが多重継承をサポートしていないことです。

これを理解するために例を見てみましょう:

Public class Transaction {
    Banking b;
    public static void main(String a[])    
    {    
        b = new Deposit();    
        if(b.deposit()){    
            b = new Credit();
            c.credit();    
        }
    }
}

知っておきたいこと:

  1. 継承はコンパイル時にその機能を提供する一方で、構成は実行時に簡単に達成されます

  2. 構成はHAS-A関係としても知られ、継承はIS-A関係としても知られています

したがって、上記のさまざまな理由から、継承よりも常に構成を優先する習慣をつけてください。


3
同意しますが、コンポジションを使用してソリューションを与えます....たとえば、ベビーシットを行う必要があります。たとえば、スーパークラスXがメソッド名をdoからdoingに変更します...次にサブクラスYも維持する必要があります(変更する必要があります)同様に)これはまだ密結合ですか?そして、どうやってそれを取り除くのですか?
stuckedoverflow

19

@Michael Rodriguesの回答は正しくないため(申し訳ありません。直接コメントすることはできません)、混乱を招く可能性があります。

インターフェースの実装は継承の一種です ... インターフェースを実装すると、すべての定数を継承するだけでなく、インターフェースで指定された型になるようにオブジェクトをコミットします。それまだ " is-a "関係です。車がFillableを実装している場合、車-」「Fillable」であり、Fillableを使用する場所であればどこでもコードで使用できます。

構成は継承とは根本的に異なります。合成を使用する場合、他の回答の注記と同様に、継承を使用して作成する「is-a」関係と対照的に、2つのオブジェクト間に「has-a」関係を作成します

それで、他の質問の車の例から、もし車が " has-a "ガソリンタンクだと言いたければ、私は次のように構成を使います:

public class Car {

private GasTank myCarsGasTank;

}

うまくいけば、誤解が解消されます。


17

継承IS-A関係を引き出します。構成HAS-A関係を引き出します。戦略パターンは、特定の動作を定義するアルゴリズムのファミリーが存在する場合にコンポジションを使用する必要があることを説明しています。
空飛ぶ行動を実装するアヒルのクラスの古典的な例。

public interface Flyable{
 public void fly();
}

public class Duck {
 Flyable fly;

 public Duck(){
  fly = new BackwardFlying();
 }
}

したがって、たとえば次のように、flyingを実装する複数のクラスを持つことができます。

public class BackwardFlying implements Flyable{
  public void fly(){
    Systemout.println("Flies backward ");
  }
}
public class FastFlying implements Flyable{
  public void fly(){
    Systemout.println("Flies 100 miles/sec");
  }
}

継承があったとしたら、フライ機能を何度も実装する2つの異なるクラスの鳥がいるでしょう。したがって、継承と構成は完全に異なります。


7

構成は、そのとおりです。部品を接続することでオブジェクトを作成します。

この回答の残りの部分を編集すると、誤って次の前提に基づいています。
これはインターフェースで実現されます。
たとえば、上の例を使用するとCar

Car implements iDrivable, iUsesFuel, iProtectsOccupants
Motorbike implements iDrivable, iUsesFuel, iShortcutThroughTraffic
House implements iProtectsOccupants
Generator implements iUsesFuel

したがって、いくつかの標準的な理論的コンポーネントを使用して、オブジェクトを構築できます。次にHouseCarがその居住者を保護する方法、およびがその居住者を保護する方法を記入するのはあなたの仕事です。

継承はその逆です。完全な(または半完全な)オブジェクトから始めて、変更するさまざまなビットを置き換えるかオーバーライドします。

たとえばMotorVehicleFuelableメソッドとDriveメソッドが付属している場合があります。バイクと車を埋めるのは同じなので、FuelメソッドはそのままにしておくことができますがDrive、バイクの運転方法が非常に異なるため、メソッドをオーバーライドできます。Car

継承を使用すると、一部のクラスはすでに完全に実装されており、他のクラスには強制的にオーバーライドするメソッドがあります。構成では何も与えられません。(ただし、何か問題が発生した場合は、他のクラスのメソッドを呼び出すことでインターフェイスを実装できます)。

iUsesFuelなどのメソッドがある場合、車であるかどうかに関係なく、燃料を供給できるオブジェクトを処理することだけを心配するメソッドを別の場所(別のクラス、別のプロジェクト)に置くことができるため、構成はより柔軟に見えます。ボート、ストーブ、バーベキューなど。インターフェイスは、そのインターフェイスを実装していると言うクラスが、そのインターフェイスに関するすべてのメソッドを実際に持つことを義務付けています。例えば、

iFuelable Interface:
   void AddSomeFuel()
   void UseSomeFuel()
   int  percentageFull()

その後、あなたはどこか他の方法を持つことができます

private void FillHerUp(iFuelable : objectToFill) {

   Do while (objectToFill.percentageFull() <= 100)  {

        objectToFill.AddSomeFuel();
   }

奇妙な例ですが、オブジェクトがを実装しているため、このメソッドはどのように満たされているかを気にしませんiUsesFuel。物語の終わり。

代わりにInheritanceを使用した場合、継承元のかなり奇妙な「ObjectThatUsesFuel」ベースオブジェクトがない限りFillHerUpMotorVehiclesand を処理するために別のメソッドが必要になりますBarbecues


Java規約では、クラス名とインターフェース名はThisCaseでなくで記述されているとされていcamelCaseます。したがって、インターフェイスに名前を付けることなどが最善IDrivableです。すべてのインターフェイスをパッケージに正しく再グループ化する場合、「I」は必要ない場合があります。
ThePyroEagle

6

構成と継承は同じですか?

彼らは同じではありません。

構成:オブジェクトのグループを、オブジェクトの単一のインスタンスと同じ方法で処理する必要があります。コンポジットの目的は、オブジェクトをツリー構造に「構成」して、部分全体の階層表すことです。

継承:クラスは、直接または間接にかかわらず、すべてのスーパークラスからフィールドとメソッドを継承します。サブクラスは、継承するメソッドをオーバーライドしたり、継承したフィールドやメソッドを非表示にしたりできます。

構成パターンを実装したい場合、Javaでそれを行うにはどうすればよいですか?

ウィキペディアの記事は、Javaで複合パターンを実装するのに十分です。

ここに画像の説明を入力してください

主要な参加者:

コンポーネント

  1. 複合コンポーネントを含むすべてのコンポーネントの抽象化です
  2. コンポジション内のオブジェクトのインターフェースを宣言します

  1. コンポジション内のリーフオブジェクトを表します
  2. すべてのコンポーネントメソッドを実装します

合成

  1. 複合コンポーネント(子を持つコンポーネント)を表します
  2. 子を操作するメソッドを実装します
  3. すべてのComponentメソッドを実装します。通常、それらを子に委任します。

複合パターンを理解するためのコード例:

import java.util.List;
import java.util.ArrayList;

interface Part{
    public double getPrice();
    public String getName();
}
class Engine implements Part{
    String name;
    double price;
    public Engine(String name,double price){
        this.name = name;
        this.price = price;
    }
    public double getPrice(){
        return price;
    }
    public String getName(){
        return name;
    }
}
class Trunk implements Part{
    String name;
    double price;
    public Trunk(String name,double price){
        this.name = name;
        this.price = price;
    }
    public double getPrice(){
        return price;
    }
    public String getName(){
        return name;
    }
}
class Body implements Part{
    String name;
    double price;
    public Body(String name,double price){
        this.name = name;
        this.price = price;
    }
    public double getPrice(){
        return price;
    }
    public String getName(){
        return name;
    }
}
class Car implements Part{
    List<Part> parts;
    String name;

    public Car(String name){
        this.name = name;
        parts = new ArrayList<Part>();
    }
    public void addPart(Part part){
        parts.add(part);
    }
    public String getName(){
        return name;
    }
    public String getPartNames(){
        StringBuilder sb = new StringBuilder();
        for ( Part part: parts){
            sb.append(part.getName()).append(" ");
        }
        return sb.toString();
    }
    public double getPrice(){
        double price = 0;
        for ( Part part: parts){
            price += part.getPrice();
        }
        return price;
    }   
}

public class CompositeDemo{
    public static void main(String args[]){
        Part engine = new Engine("DiselEngine",15000);
        Part trunk = new Trunk("Trunk",10000);
        Part body = new Body("Body",12000);

        Car car = new Car("Innova");
        car.addPart(engine);
        car.addPart(trunk);
        car.addPart(body);

        double price = car.getPrice();

        System.out.println("Car name:"+car.getName());
        System.out.println("Car parts:"+car.getPartNames());
        System.out.println("Car price:"+car.getPrice());
    }

}

出力:

Car name:Innova
Car parts:DiselEngine Trunk Body
Car price:37000.0

説明:

  1. 部分は葉です
  2. 車には多くの部品が含まれています
  3. 車のさまざまなパーツが車に追加されました
  4. 価格 =(各の価格の合計パート

構成と継承の長所と短所については、以下の質問を参照してください。

継承よりも構成を優先しますか?


車のクラス用のパーツを実装することにも意味がありますか。あなたがそれを作曲として使用する場合、それは良くないでしょう
bhalkian '20 / 03/18

4

別の例として、車のクラスを考えてみましょう。これは、構成の適切な使用法です。車は、エンジン、トランスミッション、タイヤ、シートなどを「持つ」ことになります。これらのクラスは拡張されません。


4

構成とは、何かが別個の部分で構成され、それらの部分と強い関係がある場合です。主要部分が死に、他の部分も死ぬと、彼らは自分自身の生活を持つことができません。大まかな例は人体です。心を取り除き、他のすべての部分が消えます。

継承とは、すでに存在するものを取得して使用することです。強い関係はありません。人は父親の財産を相続することができますが、それなしで行うことができます。

Javaがわからないので例を示すことはできませんが、概念の説明は提供できます。


3

1つのクラスが別のクラスを拡張する2つのクラス間の継承は、「IS A」関係を確立します。

もう一方のコンポジションには、クラス内の別のクラスのインスタンスが含まれ、「Has A」関係を確立します。作曲、それは技術的に多重継承を促進するため、Javaでは有用です。


3

単純な単語の集約では、関係があることを意味します。

合成は、集計の特殊なケースです。より具体的には、制限された集約は合成と呼ばれます。オブジェクトに他のオブジェクトが含まれている場合、含まれているオブジェクトがコンテナオブジェクトの存在なしに存在できない場合、それは合成と呼ばれます。 例:クラスに学生が含まれています。学生はクラスなしでは存在できません。クラスと学生の間には構成があります。

集計を使用する理由

コードの再利用性

集計を使用する場合

コードの再利用も、Relationshipがない場合に集約することで実現できます。

継承

継承は親子関係である継承はRelationShipであることを意味する

Javaでの継承は、1つのオブジェクトが親オブジェクトのすべてのプロパティと動作を取得するメカニズムです。

Java 1コードの再利用性における継承の使用。2子クラスとメソッドのオーバーライドに追加機能を追加します(実行時のポリモーフィズムを実現できます)。


1

継承とコンポジションの両方がコードの再利用性を提供しますが、Javaでのコンポジションと継承の主な違いは、コンポジションがコードを拡張せずに再利用できることですが、継承の場合は、コードまたは機能を再利用するためにクラスを拡張する必要があります。この事実によるもう1つの違いは、Compositionを使用することにより、拡張できない最終クラスでもコードを再利用できるが、継承ではそのような場合にコードを再利用できないことです。また、Compositionを使用すると、メンバー変数として宣言されているため、多くのクラスのコードを再利用できますが、継承では、Javaでは複数の継承がサポートされていないため、Javaでは1つのクラスしか拡張できないため、コードを再利用できます。 。ただし、1つのクラスが複数のクラスを拡張できるため、C ++でこれを行うことができます。ところで、あなたは常にJavaでの継承を超えるポジションを好むその私でさえだけではなく、ジョシュア・ブロックは、彼の本の中で提案しています


1
なぜ「継承よりも構成を行う」必要があるのですか?それらは異なる概念であり、異なる目的で使用されます。率直に言って、私はあなたがどのようにして他の人に行くことができるかさえわかりません。
クロウ

1

この例は、継承構成の違いを明確に説明していると思います。

この例では、問題は継承と構成を使用して解決されます。著者は以下の事実に注意を払います。継承、スーパークラスの変更は、それを継承すること、派生クラスで問題が発生する可能性があります。

また、UMLを継承または構成に使用した場合の表現の違いも確認できます。

http://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--which-one-should-you-choose-.html


1
リンクのみの回答は陳腐化するため推奨されません。少なくともリンクから最も重要な関連情報を回答に含めてください。
スコットソル

1

継承と構成。

継承と構成は、クラスの動作の再利用と拡張に使用されます。

IS-A関係タイプなどのファミリーアルゴリズムプログラミングモデルで主に使用される継承は、類似した種類のオブジェクトを意味します。例。

  1. ダスターは車です
  2. サファリは車です

これらはCarファミリーに属しています。

コンポジションはHAS-A関係タイプを表します。これは、Dustreが5つのギアを持っている、Safariが4つのギアを持っているなどのオブジェクトの能力を示します。既存のクラスの能力を拡張する必要があるときはいつでも、コンポジションを使用します。例では、Dusterオブジェクトにギアをもう1つ追加する必要があります。次に、ギアオブジェクトをもう1つ作成して、ダスターオブジェクトに合成する必要があります。

すべての派生クラスがこれらの機能を必要とするまで/ベースクラスを変更しないでください。このシナリオでは、Compositionを使用する必要があります。

クラスBから派生したクラスA

クラスCから派生したクラスA

クラスDから派生したクラスA

クラスAに機能を追加すると、クラスCとDがそれらの機能を必要としない場合でも、すべてのサブクラスで使用できるようになります。このシナリオでは、それらの機能用に別のクラスを作成し、必要なクラスに構成する必要があります(これがクラスBです。

以下に例を示します。

          // This is a base class
                 public abstract class Car
                    {
                       //Define prototype
                       public abstract void color();
                       public void Gear() {
                           Console.WriteLine("Car has a four Gear");
                       }
                    }


           // Here is the use of inheritence
           // This Desire class have four gears.
          //  But we need to add one more gear that is Neutral gear.

          public class Desire : Car
                   {
                       Neutral obj = null;
                       public Desire()
                       {
     // Here we are incorporating neutral gear(It is the use of composition). 
     // Now this class would have five gear. 

                           obj = new Neutral();
                           obj.NeutralGear();
                       }

                       public override void color()
                       {
                           Console.WriteLine("This is a white color car");
                       }

                   }


             // This Safari class have four gears and it is not required the neutral
             //  gear and hence we don't need to compose here.

                   public class Safari :Car{
                       public Safari()
                       { }

                       public override void color()
                       {
                           Console.WriteLine("This is a red color car");
                       }


                   }

   // This class represents the neutral gear and it would be used as a composition.

   public class Neutral {
                      public void NeutralGear() {
                           Console.WriteLine("This is a Neutral Gear");
                       }
                   }

0

合成とは、その特定のクラスと関係があるクラスにオブジェクトを作成することを意味します。学生がアカウントと関係があるとします。

継承は、これは拡張機能を備えた以前のクラスです。つまり、この新しいクラスは、拡張機能を備えた古いクラスです。学生は学生ですが、すべての学生は人間であるとします。ですから、学生と人間との関係があります。これが継承です。


0

いいえ、どちらも異なります。構成は「HAS-A」の関係に従い、継承は「IS-A」の関係に従います。作曲の最良の例は戦略的パターンでした。


2
その唯一のコメント品質
Mathews Sunny

受け入れられた回答が7年前に言ったのと同じことをコメントしただけですか?
Anjil Dhamala

0

継承とは、クラスの完全な機能を再利用することを意味します。ここで、私のクラスはスーパークラスのすべてのメソッドを使用する必要があり、私のクラスはスーパークラスと緊密に結合され、コードは継承の場合に両方のクラスで複製されます。

しかし、コンポジションを使用して別のクラスと話すとき、これらすべての問題を克服できます。コンポジションは、話したいクラスに別のクラスの属性を宣言しています。そして、その属性を使用して取得できる、そのクラスに必要な機能。

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