继承和组合的区别

组合和继承是一样的吗? 如果我想实现组合模式,我如何在Java中做到这一点?< / p >
253601 次浏览

它们完全不同。继承是一个“是一个”关系。组合是“有一个”

复合的方法是将另一个类C的实例作为类的字段,而不是扩展C。组合比继承更好的一个很好的例子是java.util.Stack,它目前扩展了java.util.Vector。现在看来,这是一个大错。一个堆栈“不是”向量;不应该允许任意插入和删除元素。应该是合成。

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

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

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

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


参见:

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

组合表示HAS A
继承意味着IS A

Example: Car 有一个引擎和Car 是一个汽车

在编程中,这被表示为:

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.
}

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

编辑这个答案的其余部分错误地基于以下前提 这是通过Interfaces.
完成的 例如,使用上面的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”基对象来继承。

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

接口实现是一种继承吗…当你实现一个接口时,你不仅继承了所有的常量,你还将你的对象提交为接口指定的类型;它仍然是一个“是一个”关系。如果汽车实现了Fillable,则汽车"是一个" Fillable,并且可以在代码中使用Fillable

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

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

public class Car {


private GasTank myCarsGasTank;


}

希望这能消除任何误解。

继承引出是一个关系。作文输出有一个关系。 策略模式解释了组合应该用于定义特定行为的算法族的情况。
典型的例子是实现飞行行为的鸭子类

public interface Flyable{
public void fly();
}


public class Duck {
Flyable fly;


public Duck(){
fly = new BackwardFlying();
}
}
因此,我们可以有多个实现飞行的类 如:< / p >
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");
}
}

如果是继承的话,我们就会有两个不同的鸟类,它们会反复实现fly函数。所以继承和组合是完全不同的。

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

另一端的作文包含类中另一个类的实例,建立了“< em >有一个< / em >”关系。作文在java中是有用的,因为它在技术上便于多重继承。

遗产怎么会是危险的?

让我们举个例子

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

1)正如上面的代码所示,类Y与类X具有很强的耦合性。如果超类X发生任何变化,Y可能会急剧断裂。假设在未来的类X中实现了一个具有以下签名的方法work

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

很高兴知道:

  1. 组合在运行时很容易实现,而继承在编译时提供其特性

  2. 组合也称为HAS-A关系,继承也称为is - a关系

因此,由于上述各种原因,要养成总是喜欢组合而不是继承的习惯。

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

在本例中,使用继承和组合解决了这个问题。作者注意到;在继承中,父类的改变可能会导致继承它的派生类出现问题。

在这里,您还可以看到使用UML进行继承或组合时在表示上的区别。

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

构图是指某物由不同的部分组成,并且它与这些部分之间有很强的关系。如果主体死亡,其他部分也会死亡,他们就不能拥有自己的生命。一个粗略的例子就是人体。取出心脏,其他部分就会消失。

继承就是你取已经存在的东西并使用它。没有很强的关系。一个人可以继承他父亲的财产,但他也可以不继承。

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

虽然继承和组合都提供了代码的可重用性,但Java中组合和继承的主要区别在于组合允许代码重用而无需扩展,但对于继承,必须扩展类以实现代码或功能的重用。来自这个事实的另一个区别是,通过使用组合,你甚至可以重用不可扩展的final类的代码,但继承在这种情况下不能重用代码。此外,通过使用组合,你可以重用来自许多类的代码,因为它们被声明为一个成员变量,但是使用继承,你可以重用来自一个类的代码,因为在Java中你只能扩展一个类,因为Java不支持多重继承。你可以在c++中这样做,因为在c++中一个类可以扩展多个类。顺便说一句,你应该总是在Java中更喜欢组合而不是继承,这不仅是我,甚至约书亚·布洛赫在他的书中建议

组合和继承是一样的吗?

它们不一样。

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

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

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

维基百科文章在java中实现复合模式足够好。

enter image description here

< em >主要参与者:< / em >

组件:

  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. < em > < / em >部分是一个叶子
  2. 汽车< em > < / em >包含许多part
  3. 汽车的不同< em > < / em >部分已添加到car
  4. 汽车< em > < / em >的价格= sum的(每个部分的价格)

关于组合和继承的利弊,请参考下面的问题。

更喜欢组合而不是继承?< / >

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

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

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

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

为什么使用聚合

代码的可重用性

当使用聚合时

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

继承

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

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

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

继承Vs组合。

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

继承主要用在IS-A一类算法编程模型中,关系类型是指相似类型的对象。的例子。

  1. 吸尘器是一辆车
  2. Safari是一辆车

这些是Car家族的。

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

除非所有派生类都需要这些功能,否则不应该对基类进行更改。对于这个场景,我们应该使用Composition。如

由类B派生

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

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

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

但是当我们用作文与其他班级进行交流时,我们可以克服这些问题。复合是将另一个类的属性声明到我们想要与之对话的类中。我们想从那个类中得到什么功能可以通过使用那个属性来实现。