组成和继承相同吗?如果要实现合成模式,如何在Java中实现呢?
组成和继承相同吗?如果要实现合成模式,如何在Java中实现呢?
Answers:
他们是绝对不同的。继承是一种“是”关系。组成是“有”。
您可以通过将另一个类的实例作为类C
的字段来进行合成,而不是扩展C
。一个很好的例子是组合比继承要好得多java.util.Stack
,目前已经扩展了java.util.Vector
。现在,这被认为是一种错误。堆栈“ is-NOT-a”向量;您不应该被允许随意插入和删除元素。它应该是合成的。
不幸的是,现在纠正该设计错误为时已晚,因为现在更改继承层次结构会破坏与现有代码的兼容性。如果Stack
使用合成而不是继承,则始终可以对其进行修改以使用其他数据结构,而不会违反API。
我强烈推荐Josh Bloch的书Effective Java 2nd Edition
好的面向对象设计并不是要自由地扩展现有的类。您的第一个本能应该是撰写。
也可以看看:
组成意味着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.
}
:-/
type
类型字段Enum
如何继承会很危险?
让我们举个例子
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引用的Test类中,我可以调用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关系
因此,出于上述各种原因,养成始终偏向于继承而不是继承的习惯。
@Michael Rodrigues给出的答案是不正确的(我很抱歉;我无法直接发表评论),并且可能导致混乱。
接口实现是继承的一种形式 ……实现接口时,不仅继承了所有常量,而且还承诺对象是接口指定的类型。仍然是“ 是 ”关系。如果一辆车实现了可输入资料,汽车“ 是,一个 ” 可输入资料,并且可以在你的代码中使用,无论你将使用可输入资料。
组合从根本上不同于继承。使用组合时,(如其他答案所述)您将在两个对象之间建立“ has-a ”关系,而不是使用继承时所建立的“ is-a ”关系。
因此,从其他问题的汽车示例中,如果我想说一辆汽车“ 有-一个 ”油箱,我将使用以下组成:
public class Car {
private GasTank myCarsGasTank;
}
希望这可以消除任何误解。
继承带来了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");
}
}
如果是继承,我们将有两类不同的鸟,它们一遍又一遍地实现fly功能。因此继承和组成完全不同。
合成就像听起来一样-您可以通过插入零件来创建对象。
根据以下前提,错误地编辑此答案的其余部分。
这是通过接口完成的。
例如,使用Car
上面的示例,
Car implements iDrivable, iUsesFuel, iProtectsOccupants
Motorbike implements iDrivable, iUsesFuel, iShortcutThroughTraffic
House implements iProtectsOccupants
Generator implements iUsesFuel
因此,通过一些标准的理论组件,您可以构建您的对象。然后,您的工作就是填写House
保护住户的方式以及Car
保护住户的方式。
继承就像相反。您从一个完整的(或半完整的)对象开始,然后替换或覆盖要更改的各个位。
例如,MotorVehicle
可能附带一种Fuelable
方法和Drive
一种方法。您可以按原样保留Fuel方法,因为填充摩托车和汽车的Drive
方法相同,但是可以覆盖该方法,因为Motorcycle的驾驶方式与的差别很大Car
。
通过继承,某些类已经完全实现,而另一些类则具有被强制重写的方法。有了成分,您什么也不会得到。(但是,如果碰巧有一些问题,可以通过调用其他类中的方法来实现接口)。
组合看起来更灵活,因为如果您拥有iUsesFuel之类的方法,那么您可以在其他地方(另一个类,另一个项目)拥有一个方法,该方法只担心处理可以加油的物体,而不管它是否是汽车,接口要求类实现它们的接口实际上具有该接口的所有方法。例如,
iFuelable Interface:
void AddSomeFuel()
void UseSomeFuel()
int percentageFull()
那么你可以在其他地方有一种方法
private void FillHerUp(iFuelable : objectToFill) {
Do while (objectToFill.percentageFull() <= 100) {
objectToFill.AddSomeFuel();
}
奇怪的示例,但是它表明此方法并不关心它填充了什么,因为该对象实现了iUsesFuel
,它可以被填充。故事结局。
如果改用继承,则将需要其他FillHerUp
方法来处理MotorVehicles
和Barbecues
,除非您有一些非常奇怪的“ ObjectThatUsesFuel”基础对象要从中继承。
ThisCase
不是编写的camelCase
。因此,最好命名您的接口IDrivable
等。如果您将所有接口正确地重新组合到一个程序包中,则可能不需要“ I”。
组成和继承相同吗?
他们不一样。
组合:它使必须以与单个对象实例相同的方式对待一组对象。组合的目的是将对象“组成”为树状结构,以表示整体层次结构
继承:类从其所有超类(无论直接还是间接)继承字段和方法。子类可以覆盖其继承的方法,也可以隐藏其继承的字段或方法。
如果要实现合成模式,如何在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
说明:
有关组成和继承的优缺点,请参见以下问题。
尽管“继承”和“组合”都提供了代码可重用性,但是Java中“组合”和“继承”之间的主要区别在于,“组合”允许在不扩展代码的情况下重用代码,但是对于继承,您必须扩展类以实现对代码或功能的任何重用。来自此事实的另一个区别是,通过使用Composition,您甚至可以为最终类重用代码,这是不可扩展的,但是在这种情况下,继承不能重用代码。同样,通过使用Composition,您可以重用许多类中的代码,因为它们被声明为成员变量,但是通过Inheritance,您只能重用一个类的代码,因为在Java中您只能扩展一个类,因为Java中不支持多重继承。 。您可以在C ++中执行此操作,因为在那里一类可以扩展一个以上的类。顺便说一句,你应该永远更喜欢Java中的继承而不是继承,它不仅是我本人,甚至约书亚·布洛赫(Joshua Bloch)都在他的书中建议过
我认为这个示例清楚地解释了继承和组合之间的区别。
例如,使用继承和组合解决了问题。作者注意到事实; 在继承中,超类的更改可能会导致继承它的派生类出现问题。
使用UML进行继承或组合时,您还可以在此处看到表示形式的差异。
继承与组成。
继承和组合都用于重用和扩展类行为。
继承主要用于家庭算法编程模型中,例如IS-A。关系类型表示类似的对象。例。
这些属于汽车家族。
组合表示HAS-A关系类型。它显示对象的功能,例如Duster具有5档,Safari具有4档等。每当我们需要扩展现有类的功能时,便使用组合。例如,我们需要在Duster对象中添加一个齿轮,然后再创建一个齿轮对象并将其组合到除尘器对象中。
除非/除非所有派生类都需要这些功能,否则我们不应该在基类中进行更改。对于这种情况,我们应该使用Composition。
B级派生的A级
C类派生的A类
由D类派生的A类。
当我们在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”关系。战略模式是构成的最好例子。
继承意味着重用一个类的全部功能。在这里,我的类必须使用超类的所有方法,并且在继承的情况下,我的类将与超类进行细致的耦合,并且两个类中的代码都将重复。
但是当我们使用写作与另一个班级交谈时,我们可以克服所有这些问题。合成是在我们要讨论的班级中声明另一个班级的属性。以及使用该属性可以从该类中获得什么功能。