ChatGPT解决这个技术问题 Extra ChatGPT

继承与组合的区别

组合和继承是一样的吗?如果我想实现组合模式,我该如何在 Java 中做到这一点?

其他相关问题:有什么组合不能完成继承可以吗? stackoverflow.com/questions/2238642/…
这篇文章对我很有用,因此很有帮助:thoughtworks.com/insights/blog/…

p
polygenelubricants

它们完全不同。继承是一种“is-a”关系。作曲是一种“有”。

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

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

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

第 16 条:优先组合而不是继承

第 17 项:设计和文件继承或禁止继承

好的面向对象设计不是自由扩展现有的类。你的第一直觉应该是作曲。

也可以看看:

组合与继承:对关联类的两种基本方式的比较研究


有趣的。为什么不直接创建一个使用组合的新类 java.util.Stack2?
我很欣赏这个答案;但是,我觉得答案似乎偏离了轨道,并且更多地研究了围绕语言(和特定包)设计的问题,而不是回答有关组合与继承的问题。我非常喜欢回答关于 SO 的问题,引用资源 - 在不提供比单行总结更深入的总结的情况下不链接到外部资源。
不好的例子,我不得不做额外的搜索来理解 Vector 和 Stack 是什么。
很好,但是您关于 java 堆栈的示例不是一个好的候选者,因为这种继承是关于选择继承而不是组合的错误决定的示例,如本文所述:thoughtworks.com/insights/blog/…
A
Andrea Bergonzo

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

我会将“汽车”更改为“动力车辆”,因为在许多解释中,汽车和汽车是等价的。
@hexafraction 我同意 Vehicle 可能是一个更好的选择,但同时 -codaddict- 已经很好地说明了这一点。
“一个引擎”:-/
很好地解释。 +1。进一步阅读javarevisited.blogspot.in/2015/06/…
@AndreyAkhmetov Automobile 可以有 Enum 类型的 type 字段
S
Samix

继承怎么可能是危险的?

举个例子

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 引用的测试类中,我可以调用 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();    
        }
    }
}

很高兴知道 :

组合在运行时很容易实现,而继承在编译时提供其特性组合也称为HAS-A关系,继承也称为IS-A关系

因此,出于上述各种原因,请养成始终喜欢组合而不是继承的习惯。


同意,但考虑到您使用组合的解决方案....我们仍然需要做一个保姆,例如现在超类 X 将方法名称从 do 更改为 do....然后子类 Y 也需要维护(需要更改以及)这仍然是紧密耦合吗?我们如何摆脱它?
@stuckedoverflow 通过名称或签名更改合同是反模式。然而,新的扩展是可以接受的。 (固体中的 O)
R
Ravindra babu

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

接口实现is a form of inheritance...当你实现一个接口时,你不仅继承了所有的常量,而且你将你的对象提交为接口指定的类型;它仍然是一种“is-a”关系。如果汽车实现了Fillable,那么汽车“is-aFillable,并且可以在您的代码中使用可填充

组合从根本上不同于继承。当您使用组合时,您(正如其他答案所指出的那样)在两个对象之间建立“has-a”关系,而不是使用继承时建立的“is-a”关系。

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

public class Car {

private GasTank myCarsGasTank;

}

希望这能消除任何误解。


S
Samix

继承带来了 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");
  }
}

如果是为了继承,我们将有两种不同的鸟类,它们一遍又一遍地实现飞行功能。所以继承和组合是完全不同的。


Duck 实施 BackwardFlying 或 BackwardFlyer 的缺点是什么?
d
dlamblin

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

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

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

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

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

例如,MotorVehicle 可能带有 Fuelable 方法和 Drive 方法。您可以保留 Fuel 方法,因为给摩托车和汽车加油是一样的,但是您可以覆盖 Drive 方法,因为 Motorbike 的行驶方式与 Car 完全不同。

通过继承,一些类已经完全实现,而另一些类则具有您被迫重写的方法。有了Composition,什么都没有给你。 (但如果你碰巧有一些东西,你可以通过调用其他类中的方法来实现接口)。

组合被认为更灵活,因为如果你有一个像 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”基础对象可以从中继承。


Java 约定规定类和接口名称写在 ThisCase 中,而不是 camelCase 中。因此,最好将您的接口命名为 IDrivable 等。如果您将所有接口正确地重新组合到一个包中,则可能不需要“I”。
C
Community

组合和继承是一样的吗?

他们不一样。

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

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

如果我想实现组合模式,我该如何在 Java 中做到这一点?

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

https://i.stack.imgur.com/DDf8y.png

主要参与者:

零件:

是所有组件的抽象,包括组合组件声明组合中对象的接口

叶子:

表示组合中的叶对象 实现所有 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

解释:

零件是一片叶子汽车包含许多零件汽车的不同零件已添加到汽车汽车的价格=总和(每个零件的价格)

有关组合和继承的优缺点,请参阅以下问题。

Prefer composition over inheritance?


为汽车类实现零件是否也有意义。拿来当作曲会不会不好
B
BlackICE

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


R
Robert Rocha

组合是指事物由不同的部分组成,并且与这些部分有很强的关系。如果主要部分死了,其他部分也死了,他们就无法拥有自己的生活。一个粗略的例子是人体。取出心脏,所有其他部分都消失了。

继承是您只需获取已经存在的东西并使用它。没有牢固的关系。一个人可以继承他父亲的遗产,但他可以没有它。

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


K
Keshav Gera

在简单的单词聚合中意味着有关系..

组合是聚合的一种特殊情况。在更具体的方式中,受限制的聚合称为组合。当一个对象包含另一个对象时,如果所包含的对象没有容器对象的存在就不能存在,则称为组合。示例:一个班级包含学生。没有班级,学生就无法存在。班级和学生之间存在着组合。

为什么使用聚合

代码可重用性

何时使用聚合

当没有 Relationship 时,代码重用也最好通过聚合来实现

遗产

继承是父子关系继承意味着是关系

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

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


R
Ravindra babu

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

组合在另一端包含另一个类的实例,在你的类中建立“Has A”关系。 java中的组合很有用,因为它在技术上促进了多重继承。


N
Nitin Pawar

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


为什么我应该“优先组合而不是继承”?它们是不同的概念,用于不同的目的。坦率地说,我不明白你怎么能从一个到另一个。
R
Ravindra babu

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

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

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

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


不鼓励仅链接的答案,因为它们过时了。请在您的答案中至少包含链接中最重要的相关信息。
S
Sheo Dayal Singh

继承与组合。

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

继承主要用在家族算法编程模型中,例如IS-A关系类型意味着相似种类的对象。例子。

除尘器是汽车 Safari 是汽车

这些属于汽车家族。

组合表示HAS-A关系类型。它显示了一个对象的能力,例如Duster有五个齿轮,Safari有四个齿轮等。当我们需要扩展现有类的能力时,使用组合。示例我们需要再添加一个除尘器对象中的齿轮然后我们必须再创建一个齿轮对象并将其组合到除尘器对象中。

我们不应该在基类中进行更改,直到/除非所有派生类都需要这些功能。对于这种情况,我们应该使用组合。例如

类由 B 类派生

由 C 类派生的 A 类

类由 D 类派生。

当我们在 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");
                       }
                   }

H
HM Nayem

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

继承是,这是具有扩展功能的前一个类。这意味着这个新类是具有一些扩展功能的旧类。假设学生是学生,但所有学生都是人。因此,学生和人之间存在关系。这是继承。


V
Vikas Kapadiya

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

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


R
Ranga Reddy

不,两者是不同的。组合遵循“HAS-A”关系,继承遵循“IS-A”关系。组合的最佳示例是战略模式。


它唯一的评论质量
您是否只是评论了在您之前 7 年接受的答案所说的话?