Difference between Inheritance and Composition
组成和继承是一样的吗?如果我想实现构图模式,我如何在Java中实现呢?
它们完全不同。继承是一种"即是即是"的关系。构图是"has-a"。
通过将另一个类
不幸的是,纠正这种设计错误为时已晚,因为现在更改继承层次结构将破坏与现有代码的兼容性。如果
我高度推荐Josh Bloch的书《有效Java第二版》
- 第16条:有利于组合而非继承
- 第十七条继承或者禁止继承的设计和文件
好的面向对象设计并不是要自由地扩展现有的类。你的第一反应应该是作曲。
参见:
- 组合与继承:比较研究两种联系类的基本方法
组成是指
在编程中,这表示为:
1 2 3 4 5 6 7 | 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. } |
继承会有多危险?
让我们举个例子
1 2 3 4 5 6 7 8 9 | public class X{ public void do(){ } } Public Class Y extends X{ public void work(){ do(); } } |
1)如上代码所示,Y类与X类有很强的耦合,如果超类X中有任何变化,Y可能会急剧破裂。假设在将来,类X实现了一个方法工作,其签名如下
1 2 | public int work(){ } |
更改在类X中完成,但会使类Y不可编译。所以这种依赖关系可以上升到任何级别,而且非常危险。每次超类可能对其所有子类中的代码没有完全的可见性,子类可能总是注意到超类中发生了什么。因此,我们需要避免这种强烈和不必要的耦合。
构图如何解决这个问题?
让我们通过修改相同的示例来查看
1 2 3 4 5 6 7 8 9 10 11 | public class X{ public void do(){ } } Public Class Y{ X x = new X(); public void work(){ x.do(); } } |
这里我们在Y类中创建X类的引用,并通过创建X类的实例来调用X类的方法。现在所有的强耦合都消失了。超类和子类现在彼此高度独立。在继承情况下,类可以自由地进行危险的更改。
2)组合的第二个非常好的优点是它提供了方法调用的灵活性,例如:
1 2 3 4 5 6 7 8 | class X implements R {} class Y implements R {} public class Test{ R r; } |
在使用r引用的测试类中,我可以调用x类和y类的方法。这种灵活性在继承中从未存在过。
3)另一大优势:单元测试
1 2 3 4 5 6 7 8 9 10 11 | public class X { public void do(){ } } Public Class Y { X x = new X(); public void work(){ x.do(); } } |
在上面的例子中,如果不知道X实例的状态,可以很容易地通过使用一些测试数据来模拟它,并且可以很容易地测试所有方法。这在继承中根本不可能实现,因为您严重依赖于超类来获取实例的状态并执行任何方法。
4)我们应该避免继承的另一个很好的原因是Java不支持多重继承。
让我们举个例子来理解这一点:
1 2 3 4 5 6 7 8 9 10 11 | Public class Transaction { Banking b; public static void main(String a[]) { b = new Deposit(); if(b.deposit()){ b = new Credit(); c.credit(); } } } |
很高兴知道:
组合在运行时很容易实现,而继承在编译时提供其特性
组合也被称为有关系,继承也被称为是关系。
因此,出于以上各种原因,使它成为一种习惯,总是喜欢组合而不是继承。
@michael rodrigues给出的答案不正确(我很抱歉,我无法直接发表评论),可能会导致一些混乱。
接口实现是继承的一种形式…当你实现一个接口时,你不仅要继承所有的常量,还要把你的对象提交给接口指定的类型;它仍然是一个"is-a"关系。如果汽车实现了可填充,那么汽车"is-a"是可填充的,并且可以在您的代码中使用任何使用可填充的地方。
组合与继承有本质的区别。当你使用组合时,你正在(另一个答案注)在两个对象之间建立"有-有"的关系,而不是你在使用继承时建立的"是-一"的关系。
因此,从其他问题中的汽车例子来看,如果我想说一辆汽车"有一个"油箱,我将使用合成,如下所示:
1 2 3 4 5 | public class Car { private GasTank myCarsGasTank; } |
希望能消除误解。
继承带来的是一种关系。构图产生了一种关系。策略模式解释了在存在定义特定行为的算法系列的情况下,应该使用组合。
实现飞行行为的Duck类的经典示例。
1 2 3 4 5 6 7 8 9 10 11 | public interface Flyable{ public void fly(); } public class Duck { Flyable fly; public Duck(){ fly = new BackwardFlying(); } } |
因此我们可以有多个类来实现飞行如:
1 2 3 4 5 6 7 8 9 10 | 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"); } } |
如果是为了继承,我们将拥有两种不同的鸟类,它们一遍又一遍地执行飞行功能。所以继承和组成是完全不同的。
构图和听起来一样——你通过插入部件来创建一个对象。
编辑此答案的其余部分是错误地基于以下前提。这是通过接口实现的。例如,使用上面的
1 2 3 4 | Car implements iDrivable, iUsesFuel, iProtectsOccupants Motorbike implements iDrivable, iUsesFuel, iShortcutThroughTraffic House implements iProtectsOccupants Generator implements iUsesFuel |
因此,使用一些标准的理论组件,您可以构建您的对象。那么你的工作就是填写一个
继承就像另一条路。从一个完整(或半完整)对象开始,替换或重写要更改的各种位。
例如,
有了继承,一些类已经完全实现了,而另一些类则具有强制重写的方法。在作曲方面,你什么都没有得到。(但是,如果您碰巧有东西在周围,您可以通过在其他类中调用方法来实现接口)。
组合被认为是更灵活的,因为如果你有一个方法,比如iusesfuel,你可以在其他地方有一个方法(另一个类,另一个项目),它只关心处理可以添加燃料的对象,不管是汽车、船、炉子、烧烤等等。接口要求那些说它们实现了inte的类rface实际上拥有该接口所涉及的方法。例如,
1 2 3 4 | iFuelable Interface: void AddSomeFuel() void UseSomeFuel() int percentageFull() |
那么你可以在别的地方找个方法
1 2 3 4 5 6 | private void FillHerUp(iFuelable : objectToFill) { Do while (objectToFill.percentageFull() <= 100) { objectToFill.AddSomeFuel(); } |
很奇怪的例子,但是它表明这个方法并不关心它要填充什么,因为对象实现了
如果使用继承,则需要不同的
Are Composition and Inheritance the same?
它们不一样。
组合:它使一组对象必须以与单个对象实例相同的方式进行处理。组合的目的是将对象"组合"成树结构,以表示部分整体层次结构。
继承:一个类从它的所有超类继承字段和方法,无论是直接的还是间接的。子类可以重写它继承的方法,也可以隐藏它继承的字段或方法。
If I want to implement the composition pattern, how can I do that in Java?
维基百科的文章足够好,可以在爪哇实现复合模式。
主要参与者:
组件:
叶:
混合成的:
理解复合模式的代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | 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()); } } |
输出:
1 2 3 | Car name:Innova Car parts:DiselEngine Trunk Body Car price:37000.0 |
说明:
关于组合和继承的利弊,请参阅下面的问题。
更喜欢组合而不是继承?
作为另一个例子,考虑一个汽车类,这将是一个很好的组合使用,一辆汽车将"有"一个发动机,一个变速器,轮胎,座椅等。它不会扩展任何这些类。
简单来说,聚合意味着有一种关系。
组合是聚合的一种特殊情况。以一种更具体的方式,一个受限制的聚合称为组合。当一个对象包含另一个对象时,如果所包含的对象不存在容器对象,则称为组合。例如:一个班级包含学生。没有班级学生是不可能存在的。学生和学生之间有作文。
为什么使用聚合
代码可重用性
使用聚合时
当没有关系时,代码重用也最好通过聚合来实现。
遗传
继承是父子关系继承意味着是一种关系
Java中的继承是一个对象获取父对象的所有属性和行为的机制。
在Java中使用继承1代码可重用性。2在子类中添加额外的特性以及方法重写(这样就可以实现运行时多态性)。
两个类之间的继承,其中一个类扩展另一个类,建立"是"关系。
另一端的组合包含类中另一个类的实例,建立"有"关系。Java中的构造函数是有用的,因为它在技术上促进了多重继承。
构图是指某物由不同的部分组成,它与这些部分有着很强的关系。如果主体死亡,其他主体也会死亡,他们就不能拥有自己的生命。人体就是一个粗略的例子。把心脏拿出来,其他的部分都消失了。
继承就是你获取已经存在的东西并使用它的地方。没有牢固的关系。一个人可以继承他父亲的遗产,但他可以不继承。
我不知道Java,所以我不能提供一个例子,但我可以提供一个概念的解释。
虽然继承和组合都提供代码可重用性,但Java中的组成和继承的主要区别在于,构图允许在不扩展代码的情况下重用代码,但是对于继承,必须扩展类以重用代码或功能。这一事实的另一个区别是,通过使用组合,您可以重用代码,即使是不可扩展但继承在这种情况下也不能重用代码的最终类。此外,通过使用组合,您可以重用许多类中的代码,因为它们被声明为一个成员变量,但是继承可以重用一个类的代码形式,因为在爪哇中只能扩展一个类,因为Java中不支持多重继承。你可以在C++中这样做,因为有一个类可以扩展不止一个类。顺便说一下,在Java中,你应该更喜欢构图而不是继承,不仅仅是我,甚至Joshua Bloch也在他的书中提出过。
继承与组合。
继承和组合都用于类行为的可重用性和扩展。
继承主要用于族算法编程模型中,如IS-A关系类型表示类似的对象。例子。
这些是汽车家族的。
组合表示has-a关系类型。它表示一个对象的能力,如duster有五个齿轮,safari有四个齿轮等。每当我们需要扩展现有类的能力时,就使用组合。例如,我们需要在duster对象中再添加一个齿轮,然后我们必须再创建一个齿轮对象并将其组合到duster o。投掷。
除非所有派生类都需要这些功能,否则我们不应该在基类中进行更改。对于这种情况,我们应该使用组合。例如
由B类派生的A类
类C派生的类A
类D派生的类A。
当我们在A类中添加任何功能时,即使C类和D类不需要这些功能,它也可用于所有子类。对于这种情况,我们需要为这些功能创建一个单独的类,并将其组合到所需的类(这里是B类)。
示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | // 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"); } } |
我认为这个例子清楚地解释了继承和组合之间的区别。
在这个例子中,使用继承和组合解决了这个问题。作者注意到,在继承中,超类的变化可能会导致继承它的派生类出现问题。
在那里,当您使用UML进行继承或组合时,您还可以看到表示上的差异。
http://www. javaWork.com /文章/ 2076814 /核心Java/Grave/Wrange-您应该选择哪个?
继承意味着重用一个类的完整功能,在这里,我的类必须使用超级类的所有方法,我的类将与超级类紧密耦合,如果继承,代码将在两个类中复制。
但当我们用作文和另一个班级交谈时,我们可以克服所有这些问题。构图正在将另一个类的属性声明到我们要讨论的类中。以及使用该属性可以从类中获得什么功能。
组合意味着创建一个与特定类相关的类的对象。假设学生与会计有关系;
继承是,这是具有扩展功能的前一个类。这意味着这个新类是具有一些扩展特性的旧类。假设学生是学生,但所有学生都是人。因此,学生和人类之间存在着某种关系。这是遗产。
不,两者都不同。组合跟随"有-有"关系和继承跟随"是-一"关系。构图最好的例子是战略模式。