继承与构成的区别

时间:2010-03-08 05:54:16

标签: java oop inheritance composition

组成和继承是否相同? 如果我想实现组合模式,我该如何在Java中实现呢?

18 个答案:

答案 0 :(得分:274)

他们完全不同。继承是“is-a”关系。作文是“has-a”

通过将另一个类C的实例作为类的字段来进行合成,而不是扩展C。组合比继承更好的一个很好的例子是java.util.Stack,目前扩展java.util.Vector。现在这被认为是一个大错。堆栈“is-NOT-a”向量;你不应该被允许任意插入和删除元素。它应该是组合而已。

不幸的是,现在纠正这个设计错误为时已晚,因为现在更改继承层次结构会破坏与现有代码的兼容性。 如果Stack使用了合成而不是继承,则可以始终修改它以使用其他数据结构而不违反API

我强烈推荐Josh Bloch的书 Effective Java 2nd Edition

  • 第16项:偏爱继承的构成
  • 第17项:继承的设计和文件,或者禁止它

良好的面向对象设计并不是要自由扩展现有的类。你的第一直觉应该是改编。


另见:

答案 1 :(得分:179)

组成意味着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.
}

答案 2 :(得分:32)

继承如何危险?

让我们举个例子

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)组合的第二个非常好的优点在于它提供了调用灵活性的方法,例如:

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)我们应该避免继承的另一个好理由是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. 因此,出于上述各种原因,习惯于总是更喜欢构图而不是继承。

答案 3 :(得分:19)

@Michael Rodrigues给出的答案是不正确的(我道歉;我无法直接评论),并可能导致一些混乱。

接口实现is a form of inheritance ...当您实现接口时,您不仅要继承所有常量,而且要将对象提交为接口指定的类型;它仍然是一种“ is-a ”关系。如果汽车实施 Fillable ,汽车“ Fillable ,并且可以在您使用可输入

组合与继承根本不同。 使用合成时,您(在其他答案中注明)在两个对象之间建立“ has-a ”关系,而不是“ is-a “使用继承时所做的关系

所以,从其他问题的汽车示例中,如果我想说汽车“有一个”的油箱,我会使用组合,如下:

public class Car {

private GasTank myCarsGasTank;

}

希望能解决任何误解。

答案 4 :(得分:14)

继承带来 IS-A 关系。 合成带来 HAS-A关系。 策略模式解释组合应该用于存在定义特定行为的算法族的情况。
经典示例是实现飞行行为的鸭类。

public interface Flyable{
 public void fly();
}

public class Duck {
 Flyable fly;

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

因此我们可以有多个实现飞行的类 例如:

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");
  }
}

如果是继承,我们会有两种不同类型的鸟类一遍又一遍地实现飞行功能。所以继承和构成完全不同。

答案 5 :(得分:7)

组合就像听起来一样 - 通过插入部件来创建对象。

编辑此答案的其余部分错误地基于以下前提 这是通过接口实现的 例如,使用上面的Car示例

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

因此,使用一些标准的理论组件,您可以构建您的对象。然后,您的工作就是填写House如何保护其占用者,以及Car如何保护其占用者。

继承就像反过来一样。您从一个完整的(或半完成的)对象开始,然后替换或覆盖您想要更改的各个位。

例如,MotorVehicle可能带有Fuelable方法和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,它可以被填充。故事结束。

如果您使用了继承,则需要使用不同的FillHerUp方法来处理MotorVehiclesBarbecues,除非您有一些相当奇怪的“ObjectThatUsesFuel”基础对象可以从中继承。

答案 6 :(得分:5)

  

组成和继承是否相同?

他们不一样。

Composition:它使一组对象必须以与单个对象实例相同的方式处理。复合的目的是将对象“组合”成树结构,以 表示部分整体层次结构

Inheritance:一个类从其所有超类继承字段和方法,无论是直接还是间接。子类可以覆盖它继承的方法,也可以隐藏它继承的字段或方法。

  

如果我想实现组合模式,我该如何在Java中实现?

Wikipedia文章足以在java中实现复合模式。

enter image description here

主要参与者:

<强>组件

  1. 是所有组件的抽象,包括复合组件
  2. 声明组合
  3. 中对象的界面

    <强>叶

    1. 表示组合
    2. 中的叶对象
    3. 实施所有组件方法
    4. <强>组合

      1. 表示复合组件(具有子组件的组件)
      2. 实施操纵儿童的方法
      3. 实现所有Component方法,通常是将它们委托给子级
      4. 了解复合模式的代码示例:

        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. Car 的价格 =(每个部分的价格)的总和
        5. 请参阅下面的问题,了解组成和继承的优点和缺点。

          Prefer composition over inheritance?

答案 7 :(得分:4)

作为另一个例子,考虑一个汽车类,这将是一个很好的组合,汽车将“拥有”发动机,变速器,轮胎,座椅等。它不会扩展任何这些类。

答案 8 :(得分:4)

简单字聚合意味着有关系..

组合是聚合的特例。以更具体的方式,受限制的聚合称为组合。当一个对象包含另一个对象时,如果所包含的对象不存在而不存在容器对象,那么它就称为组合。 示例:课程包含学生。没有课,学生就不能存在。课堂与学生之间存在着作文。

为何使用汇总

代码可重用性

何时使用聚合

当没有关系船

时,最好通过聚合实现代码重用

<强>继承

继承是父子关系继承手段是一种关系

java中的继承是一种机制,其中一个对象获取父对象的所有属性和行为。

在Java中使用继承 1代码可重用性。 2在子类中添加额外功能以及方法覆盖(因此可以实现运行时多态)。

答案 9 :(得分:3)

组合是某些东西由不同的部分组成,并且与这些部分有很强的关系。如果主要部分死于其他部分,那么它们就不能拥有自己的生命。一个粗略的例子是人体。取出心脏,所有其他部分消失。

继承是您只需要已经存在并使用它的地方。没有牢固的关系。一个人可以继承他父亲的财产,但他可以没有它。

我不懂Java所以我不能提供一个例子,但我可以提供概念的解释。

答案 10 :(得分:3)

两个类之间的

继承,其中一个类扩展另一个类,建立“ IS A ”关系。

另一端的

组合包含您班级中另一个班级的实例,建立“ 有一个 ”的关系。 java中的组合很有用,因为它在技术上有利于多重继承。

答案 11 :(得分:1)

尽管继承和组合都提供了代码可重用性,但Java中的组合和继承之间的主要区别在于,Composition允许重用代码而不扩展它,但对于继承,您必须扩展该类以便重用代码或功能。这个事实的另一个不同之处在于,通过使用Composition,您可以重用代码,即使是最终类也是不可扩展的,但在这种情况下,继承不能重用代码。另外,通过使用Composition,您可以重用来自许多类的代码,因为它们只被声明为成员变量,但是使用继承,您可以只重用一个类的代码,因为在Java中您只能扩展一个类,因为Java中不支持多个继承。您可以在C ++中执行此操作,因为有一个类可以扩展多个类。顺便说一下,你应该总是更喜欢Java中的继承而不是我,不仅仅是我,甚至 Joshua Bloch 在他的书中提出

答案 12 :(得分:1)

我认为这个例子清楚地解释了继承组合之间的区别。

在这个例子中,使用继承和组合来解决问题。作者注意到这样一个事实;在继承中,超类中的更改可能会导致继承它的派生类出现问题。

当您使用UML进行继承或合成时,您还可以看到表示的差异。

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

答案 13 :(得分:1)

继承与成分。

继承和组合都用于重用可用性和类行为的扩展。

继承主要用于家庭算法编程模型,如IS-A关系类型意味着类似的对象。实施例。

  1. Duster是一辆车
  2. Safari是一辆车
  3. 这些属于Car family。

    组合代表HAS-A关系Type.It显示对象的能力,如Duster有五个Gears,Safari有四个Gears等。每当我们需要扩展现有类的能力然后使用组合。示例我们需要在Duster对象中再添加一个齿轮然后我们必须创建一个齿轮对象并将其组合到duster对象。

    除非所有派生类都需要这些功能,否则我们不应该在基类中进行更改。对于这种情况,我们应该使用Composition.Such

    A类派生的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");
                           }
                       }
    

答案 14 :(得分:0)

组合意味着创建一个与该特定类有关系的类的对象。 假设学生与账户有关系;

继承是,这是具有扩展功能的上一个类。这意味着这个新类是具有一些扩展功能的Old类。 假设学生是学生,但所有学生都是人。所以与学生和人类有关系。这是继承。

答案 15 :(得分:0)

不,两者都不一样。组成如下&#34; HAS-A&#34;关系和继承遵循&#34; IS-A&#34;关系。组成的最佳例子是战略模式。

答案 16 :(得分:0)

继承意味着重用一个类的完整功能,这里我的类必须使用超类的所有方法,并且我的类将与超类紧密耦合,并且在继承的情况下代码将在两个类中重复

但是当我们使用构图与另一个班级交谈时,我们可以克服所有这些问题。组合将另一个类的属性声明到我们想要讨论的类中。我们可以通过使用该属性从该类中获得什么功能。

答案 17 :(得分:0)

组成

  • 是组合简单对象或数据类型的一种方法 变成更多复杂
  • 如果两个类之间存在组合关系 一个班级的字段由另一个班级组成
  • 称为具有关系

继承

  • 表面上,继承是重用
  • 的代码
  • 我们可以扩展已经用 可管理的方式。
  • 继承更多,它支持多态 语言水平!
  • 使信息易于管理 订购
  • 有了继承,新类可以从 现有课程作为构建模块
  • 新类从继承继承的属性和方法 现有课程
  • 使用扩展实施关键字
  • 有一个类层次结构(直接/间接/单个/多个)