关于java:继承和组合之间的区别

Difference between Inheritance and Composition

组成和继承是一样的吗?如果我想实现构图模式,我如何在Java中实现呢?


它们完全不同。继承是一种"即是即是"的关系。构图是"has-a"。

通过将另一个类C的实例作为类的字段来进行组合,而不是扩展C。组合比继承要好得多的一个好例子是java.util.Stack,它目前扩展了java.util.Vector。这现在被认为是一个错误。堆栈"is-not-a"向量;不应允许您随意插入和删除元素。应该是作文。

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

我高度推荐Josh Bloch的书《有效Java第二版》

  • 第16条:有利于组合而非继承
  • 第十七条继承或者禁止继承的设计和文件

好的面向对象设计并不是要自由地扩展现有的类。你的第一反应应该是作曲。

参见:

  • 组合与继承:比较研究两种联系类的基本方法


组成是指HAS A。"继承"是指IS A

Example:汽车有发动机,汽车是汽车

在编程中,这表示为:

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

    如果是为了继承,我们将拥有两种不同的鸟类,它们一遍又一遍地执行飞行功能。所以继承和组成是完全不同的。


    构图和听起来一样——你通过插入部件来创建一个对象。

    编辑此答案的其余部分是错误地基于以下前提。这是通过接口实现的。例如,使用上面的Car示例,

    1
    2
    3
    4
    Car implements iDrivable, iUsesFuel, iProtectsOccupants
    Motorbike implements iDrivable, iUsesFuel, iShortcutThroughTraffic
    House implements iProtectsOccupants
    Generator implements iUsesFuel

    因此,使用一些标准的理论组件,您可以构建您的对象。那么你的工作就是填写一个House如何保护它的使用者,以及一个Car如何保护它的使用者。

    继承就像另一条路。从一个完整(或半完整)对象开始,替换或重写要更改的各种位。

    例如,MotorVehicle可以使用Fuelable方法和Drive方法。你可以不使用燃料法,因为给摩托车和汽车加油是一样的,但是你可以使用Drive方法,因为摩托车的行驶方式与Car非常不同。

    有了继承,一些类已经完全实现了,而另一些类则具有强制重写的方法。在作曲方面,你什么都没有得到。(但是,如果您碰巧有东西在周围,您可以通过在其他类中调用方法来实现接口)。

    组合被认为是更灵活的,因为如果你有一个方法,比如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();
       }

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

    如果使用继承,则需要不同的FillHerUp方法来处理MotorVehiclesBarbecues,除非您有一些相当奇怪的"objects that usesfuel"基对象从中继承。


    Are Composition and Inheritance the same?

    它们不一样。

    组合:它使一组对象必须以与单个对象实例相同的方式进行处理。组合的目的是将对象"组合"成树结构,以表示部分整体层次结构。

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

    If I want to implement the composition pattern, how can I do that in Java?

    维基百科的文章足够好,可以在爪哇实现复合模式。

    enter image description here

    主要参与者:

    组件:

  • 是所有组件的抽象,包括复合组件
  • 声明组合中对象的接口
  • 叶:

  • 表示组合中的叶对象
  • 实现所有组件方法
  • 混合成的:

  • 表示复合组件(具有子级的组件)
  • 实现用于操作子级的方法
  • 实现所有组件方法,通常通过将它们委托给其子级来实现
  • 理解复合模式的代码示例:

    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-您应该选择哪个?


    继承意味着重用一个类的完整功能,在这里,我的类必须使用超级类的所有方法,我的类将与超级类紧密耦合,如果继承,代码将在两个类中复制。

    但当我们用作文和另一个班级交谈时,我们可以克服所有这些问题。构图正在将另一个类的属性声明到我们要讨论的类中。以及使用该属性可以从类中获得什么功能。


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

    继承是,这是具有扩展功能的前一个类。这意味着这个新类是具有一些扩展特性的旧类。假设学生是学生,但所有学生都是人。因此,学生和人类之间存在着某种关系。这是遗产。


    不,两者都不同。组合跟随"有-有"关系和继承跟随"是-一"关系。构图最好的例子是战略模式。