inheritance与构成的区别
构成和inheritance是一样的吗? 如果我想实现组合模式,我怎样才能在Java中做到这一点?
他们是完全不同的。 inheritance是一个“是”的关系。 作文是一个“有一个” 。
你通过让另一个C类的实例作为你的类的一个字段来进行组合,而不是扩展C 一个很好的例子是组合比inheritance好得多的是java.util.Stack ,它现在扩展了java.util.Vector 。 这现在被认为是一个错误。 堆栈“不是一个”vector; 你不应该被允许任意插入和删除元素。 它应该是组成而不是。
不幸的是现在纠正这个devise错误为时已晚,因为现在改变inheritance层次将会破坏与现有代码的兼容性。 如果Stack使用了组合而不是inheritance,它总是可以被修改为使用另一个数据结构而不违反API 。
我强烈推荐Josh Bloch的书“ Effective Java 2nd Edition”
- 第16项:赞成inheritance
- 第17项:inheritance的devise和文件,否则禁止
良好的面向对象的devise不是自由地扩展现有的类。 你的第一本能应该是写作。
也可以看看:
- 作文与传承:两种基本types关系的比较研究
组成意味着有HAS A
inheritance意味着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. }
@Michael Rodrigues给出的答案是不正确的(我很抱歉,我不能直接发表评论),并可能导致一些混淆。
接口实现是inheritance的一种forms …当你实现一个接口时,你不仅inheritance了所有的常量,而且你提交的对象是接口指定的types, 它仍然是一个“ 是 ”的关系。 如果一辆汽车使用了Fillable ,那么这辆汽车就是 “ Fillable” ,可以在任何你使用Fillable的地方使用。
构成与inheritance是根本不同的。 当你使用组合时,你(和另一个答案一样)在两个对象之间build立一个“ 有一个 ”关系,而不是你在使用inheritance时所做的“ is-a ”关系 。
所以,从其他问题的汽车示例中,如果我想说一辆汽车“ 有一个 ”汽油箱,我将使用组合,如下所示:
public class Car { private GasTank myCarsGasTank; }
希望清除任何误解。
inheritance带出IS-A关系。 构图带出HAS-A关系 。 statergy模式解释说,组合应该用于定义特定行为的algorithm族。
经典示例是实现飞行行为的鸭子类。
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"); } }
如果是为了inheritance,我们将有两种不同types的鸟类一次又一次地执行飞行function。因此,inheritance和组合是完全不同的。
如何inheritance可能是危险的?
让我们举个例子
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(); } }
这里我们通过创buildX类的实例来创buildY类中的X类的引用并调用X类的方法。 现在所有那强大的联结都消失了。 超类和子类现在是高度独立的。 class级可以自由地进行在inheritance情况下危险的改变。
2)第二个非常好的组合,因为它提供了方法调用灵活性例如
class X implements R {} class Y implements R {} public class Test{ R r; }
在使用r引用的Test类中,我可以调用X类的方法以及Y类。 这种灵活性从来就不存在于inheritance中
3)另一个很大的优点:unit testing
public class X{ public void do(){ } } Public Class Y{ X x=new X(); public void work(){ x.do(); } }
在上面的例子中,如果x状态的状态是未知的,它可以很容易地使用一些testing数据来模拟,所有的方法都可以很容易地testing。 这在inheritance中根本不可能因为你严重依赖于超类来获取实例的状态并执行任何方法。
4)我们应该避免inheritance的另一个好的理由是Java不支持多重inheritance。
让我们举一个例子来理解这个:
Public class Transaction { Banking b; public static void main(String a[]) { b=new Deposit(); if(b.deposit()){ b=new Credit(); c.credit(); } } }
很高兴知道 :
-
组合在运行时很容易实现,而inheritance在编译时提供它的特性
-
组成也被称为HAS-A关系,inheritance也被称为IS-A关系
因此,由于以上各种原因,习惯于总是偏好合成而不是inheritance。
组成就像听起来一样 – 你通过插入部分来创build一个对象。
编辑这个答案的其余部分是错误地基于以下前提。
这是通过接口完成的。
例如,使用上面的Car例子,
Car implements iDrivable, iUsesFuel, iProtectsOccupants Motorbike implements iDrivable, iUsesFuel, iShortcutThroughTraffic House implements iProtectsOccupants Generator implements iUsesFuel
所以用一些标准的理论组件,你可以build立你的对象。 那么你的工作就是填补一个House如何保护住户,以及Car如何保护住户。
inheritance就像是相反的方式。 您从一个完整的(或半完整的)对象开始,然后replace或覆盖您想要更改的各个位。
例如, MotorVehicle可能会带有一个Fuelable方法和Drive方法。 您可以保持燃油的方法,因为它是一样的,以填充摩托车和汽车,但是你可以超越Drive方法,因为摩托车驾驶与Car非常不同。
通过inheritance,一些类已经完全实现了,而其他类则有你不得不覆盖的方法。 随着作文没有给你。 (但你可以通过调用其他类中的方法来实现接口,如果你碰巧有东西在放置的话)。
构图被认为更灵活,因为如果你有一个方法,比如iUsesFuel,你可以在其他地方(另一个类,另一个项目),只是担心处理可以加油的对象,不pipe它是汽车,船,炉子,烧烤等。接口要求那些说他们实现这个接口的类实际上有这个接口的方法。 例如,
iFuelable Interface: void AddSomeFuel() void UseSomeFuel() int percentageFull()
那么你可以有一个方法在别的地方
private void FillHerUp(iFuelable : objectToFill) { Do while (objectToFill.percentageFull() <= 100) { objectToFill.AddSomeFuel(); }
奇怪的例子,但它表明,这种方法不关心它填满,因为对象实现iUsesFuel ,它可以填充。 故事结局。
如果使用了Inheritance,则需要使用不同的FillHerUp方法来处理MotorVehicles和Barbecues ,除非您有一些相当奇怪的“ObjectThatUsesFuel”基础对象来inheritance。
构成和inheritance是一样的吗?
他们不一样。
组成 :它使得一组对象必须以与对象的单个实例相同的方式来处理。 组合的目的是将对象“合成”到树结构中以表示部分 – 整体层次结构
inheritance :类inheritance所有超类的字段和方法,无论是直接的还是间接的。 子类可以覆盖inheritance的方法,也可以隐藏它inheritance的字段或方法。
如果我想实现组合模式,我怎样才能在Java中做到这一点?
维基百科的文章足以在java中实现复合模式。
主要参与者:
组件 :
- 是所有组件的抽象,包括复合组件
- 声明组合中对象的接口
叶子 :
- 表示构图中的叶子对象
- 实现所有组件方法
复合 :
- 表示复合组件(具有子组件)
- 实现方法来操纵孩子
- 实现所有组件方法,通常是将它们委派给它的子组件
代码示例了解复合模式:
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
说明:
- 部分是一片叶子
- 汽车包含很多零件
- 汽车的不同部分已被添加到汽车
- Car的价格=(各部分的价格)
请参阅下面的问题的构成和inheritance的利弊。
比inheritance更喜欢构图?
作为另一个例子,考虑一个汽车类,这将是一个很好的利用组成,一辆汽车将“有”发动机,变速器,轮胎,座椅等,它不会延伸任何类。
构图是由不同部分组成的东西,它与这些部分有很强的关系。 如果主要部分死了,其他人就不能拥有自己的生命。 一个粗略的例子是人体。 拿出心脏,其他所有部分消失。
inheritance就是你只需要取得已经存在的东西并使用它的地方。 没有牢固的关系。 一个人可以inheritance他的父亲的遗产,但他可以没有它。
我不知道Java,所以我不能提供一个例子,但我可以提供一个概念的解释。
尽pipeinheritance和组合都提供了代码可重用性,但是Java中的Composition和Inheritance的主要区别在于,Composition允许在不扩展的情况下重用代码,但对于inheritance,您必须扩展该类以重用代码或function。 另一个来自这个事实的差异是,通过使用Composition,你可以重用代码甚至是不可扩展的final类,但在这种情况下,Inheritance不能重用代码。 同样,通过使用Composition,您可以重用许多类中的代码,因为它们被声明为只是一个成员variables,但通过inheritance,您可以只重用一个类的代码forms,因为在Java中,只能扩展一个类,因为Java中不支持多个inheritance。 你可以用C ++来做到这一点,因为有一个类可以扩展多个类。 顺便说一下 ,你应该总是喜欢Java中的Composition over Inheritance ,它不仅仅是我,甚至还有Joshua Bloch在他的书中提到的
两个类之间的inheritance ,其中一个类扩展另一个类,build立“ IS A ”关系。
在另一端的组成包含另一个类的实例,在你的类中build立“ 有一个 ”的关系。 在java中的组成是有用的,因为它在技术上有利于多重inheritance。
我认为这个例子清楚地解释了inheritance和构成之间的差异。
在这个例子中,问题是通过inheritance和组合来解决的。 笔者注意到这样一个事实, 在inheritance中 ,超类的改变可能导致派生类中的问题,inheritance它。
在那里,当你使用UML进行inheritance或组合时,你也可以看到表示的不同。
在简单的词聚合手段有一个关系..
构图是聚合的一个特例 。 更具体地说,受限制的聚合被称为组合。 当一个对象包含另一个对象时,如果所包含的对象在没有容器对象存在的情况下不能存在,则称为组合。 例如:一个class级包含学生。 没有一个class级,学生就不能存在。 class上和学生之间存在着构成。
为什么要使用聚合
代码可重用性
使用聚合时
当没有关系时,代码重用也最好通过聚合来实现
遗产
inheritance是一个父子关系inheritance手段是一个关系
java中的inheritance是一个对象获取父对象的所有属性和行为的机制。
在Java中使用inheritance1代码可重用性。 2在子类中添加额外的function以及方法重写(这样可以实现运行时多态性)。
inheritance与构成。
inheritance和构成都被用于类行为的可重用性和扩展性。
遗传主要用于家族algorithm编程模型,如IS-A关系types意味着类似的对象。 例。
- 除尘器是一辆汽车
- Safari是一辆汽车
这些都属于汽车家族。
构图表示HAS-A关系types。它表示一个对象的能力,例如Duster有五个齿轮,Safari有四个齿轮等等。每当我们需要扩展现有类的能力,然后使用组合。 例如,我们需要在Duster对象中添加一个齿轮,那么我们必须创build一个齿轮对象并将其组成到Duster对象。
除非所有派生类需要这些function,否则我们不应该在基类中进行更改。对于这种情况,我们应该使用Composition.Such
A类由B类派生
由C类派生的A类
A类由D类派生
当我们在类A中添加任何function时,即使在类C和类D不需要这些function的情况下,它也可用于所有子类。对于这种情况,我们需要为这些function创build一个单独的类,并将其组合到所需的类中这里是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"); } }
组合意味着创build一个对象到一个与该特定类有关系的类。 假设学生与账户有关系;
inheritance是,这是具有扩展function的以前的类。 这意味着这个新class级是带有一些扩展function的老class级。 假设学生是学生,但所有学生都是人。 所以和学生和人类有关系。 这是inheritance。