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


当前回答

合成就像它听起来一样-你通过插入部分来创建一个对象。

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

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

用一些标准的理论组件,你就可以构建你的对象。然后,你的工作就是填写房子如何保护它的居住者,以及汽车如何保护它的居住者。

继承则正好相反。您从一个完整(或半完整)对象开始,然后替换或覆盖您想要更改的各种位。

例如,机动车辆可能带有可燃料方法和驱动方法。您可以保留Fuel方法,因为给摩托车和汽车加油是一样的,但您可以重写Drive方法,因为摩托车的运行方式与汽车非常不同。

通过继承,一些类已经完全实现,而其他类的方法必须重写。在合成中什么都没有给你。(但是你可以通过调用其他类中的方法来实现接口,如果你碰巧有一些东西的话)。

组合被认为是更灵活的,因为如果你有一个像iUsesFuel这样的方法,你可以在其他地方有一个方法(另一个类,另一个项目),它只关心处理可以被燃料的对象,不管它是汽车、船、炉子、烧烤等等。接口要求那些声称实现了该接口的类实际上拥有该接口的全部方法。例如,

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

然后你可以在其他地方有一个方法

private void FillHerUp(iFuelable : objectToFill) {

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

        objectToFill.AddSomeFuel();
   }

奇怪的例子,但它表明这个方法并不关心它在填充什么,因为对象实现了iUsesFuel,它可以被填充。故事结束了。

如果你使用继承,你将需要不同的FillHerUp方法来处理机动车辆和烧烤,除非你有一些相当奇怪的“ObjectThatUsesFuel”基础对象来继承。

其他回答

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

复合是聚合的一种特殊情况。在更具体的方式中,受限聚合称为组合。当一个对象包含另一个对象时,如果被包含的对象没有容器对象的存在就不能存在,那么它被称为组合。 示例:一个类包含学生。学生离不开课堂。课堂和学生之间存在着互动。

为什么使用聚合

代码的可重用性

当使用聚合时

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

继承

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

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

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

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

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

复合意味着创建一个与特定类有关系的类的对象。 假设学生与会计有关系;

继承是,这是带有扩展特性的前一个类。这意味着这个新类是具有一些扩展特性的旧类。 假设学生是学生,但所有的学生都是人。所以这是学生和人类之间的关系。这就是继承。

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

接口实现是一种继承形式…当你实现一个接口时,你不仅继承了所有的常量,你还将你的对象提交为接口指定的类型;这仍然是一种“是”的关系。如果汽车实现了Fillable,那么汽车“是-a”Fillable,并且可以在任何需要使用Fillable的代码中使用。

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

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

public class Car {

private GasTank myCarsGasTank;

}

希望这能消除任何误解。

它们完全不同。继承是一种“是-是”关系。作文是“has-a”。

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

不幸的是,现在纠正这个设计错误已经太晚了,因为现在更改继承层次结构将破坏与现有代码的兼容性。如果Stack使用组合而不是继承,那么它总是可以被修改为使用另一种数据结构,而不会违反API。

我强烈推荐Josh Bloch的《Effective Java第二版》

项目16:比起继承,更喜欢组合 项目17:设计和文件的继承或禁止

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


参见:

组合与继承:两种基本的类关联方法的比较