【问题标题】:Advantages of composition over inheritance in JavaJava中组合优于继承的优点
【发布时间】:2016-12-08 13:34:42
【问题描述】:

以下段落引自 Bruce Eckel 的“Thinking in Java”一书。

组合具有很大的灵活性。新类的成员对象通常是私有的,使使用该类的客户端程序员无法访问它们。这允许您在不影响现有客户端代码的情况下更改这些成员。 您还可以在运行时更改成员对象,以动态更改程序的行为。接下来要介绍的继承没有这种灵活性,因为编译器必须对使用继承创建的类设置编译时限制

我不明白,如何动态更改成员对象,以及它如何优于继承。 有人可以解释一下吗

【问题讨论】:

  • 成员对象可通过引用字段获得,如果它们是可变的,您可以使用赋值来更改它们,即=
  • 能否给个代码示例
  • 为什么投反对票?
  • 您能否澄清您对分配和使用字段的疑问?如果不清楚 OP 在寻找什么,有些人会否决问题。我发表评论...
  • 我明白你的意思。我只想看一个有用的例子,在这种方式中使用组合,如上所述,比使用继承更有优势。

标签: java


【解决方案1】:

如果您的类扩展了ArrayList,则您的类的用户可以访问ArrayList 的所有公共方法。您不能更改您的类来扩展 LinkedList,因为这可能会破坏现有代码。

 public class Inheritence extends ArrayList<String>
 {
     // here your class depends on ArrayList implementation
 }

另一方面,如果您的类有一个类型为List 的私有成员,则您的类的用户不能直接调用该成员的方法。您可以在运行时决定是否将ArrayList 实例分配给该成员,或者分配不同的List 实现(LinkedList 或其他)。

public class Composition
{
    private List<String> member;

    public Composition ()
    {
        // here you decide the type of your member during runtime
        if (someCondition) {
            member = new ArrayList<String>();
        } else {
            member = new LinkedList<String>();
        }
    }
}

【讨论】:

    【解决方案2】:

    如果你使用继承,你几乎看不到全貌,你必须大量研究代码地狱才能理解发生了什么。如果你在父类中改变一件小事,你就有麻烦了。所有的后代呢?这种变化对他们有影响吗?

    还可以考虑测试。父类不能分开,你必须测试所有东西,测试很复杂,而且往往根本不做,因为工作量太大而没有测试意义。

    另一方面,当你使用组合时,你可以很容易地分离功能,你可以使用依赖注入,当你阅读别人的代码时,你可以专注于更小的功能。如果使用组合的更高级别的类需要用其他实现替换一些次要功能,那么它会容易得多,因为您更好地知道代码中发生了什么。分散在几个继承级别的代码没有被封装,而且常常完全不可读。

    继承也是危险的。举个例子,当你想计算时,你向 HashSet 添加了多少项。您可以覆盖方法 add 和 addAll,但您不知道 addAll 是否为每个项目调用方法 add。看到这个example

    要回答您最初的问题 - 您可以通过构造函数或设置器传递内部对象。

    【讨论】:

      【解决方案3】:

      查看组合和继承的更广泛的方式是,组合是一种实现 has-a 关系的设计技术,而继承是一种实现 is-a 关系的技术.让我们进入面向对象的角度来更好地理解这一点。

      当我说Dog 是一个 MammalSnake 是一个 Reptile 但两者都是Animals,设计可以像,应该有一个名为Animal 的超类和两个名为MammalReptile 的子类,其中DogSnake 应该被扩展。相应代码如下:

      public abstract class Animal {
          public abstract void move();
          public abstract void sleep();
          public abstract void reproduce();
      
      }
      

      任何动物都可以执行这三个动作。

      public abstract class Mammal extends Animal{
          @Override
          public void reproduce(){
              System.out.println("Gives birth!!");
          }
      
      }
      
      public abstract class Reptile extends Animal{
          @Override
          public void reproduce(){
              System.out.println("Lays Eggs!!");
          }
      }
      

      所有哺乳动物/爬行动物都会以共同的方式执行这个定义的功能。

      public class Dog extends Mammal{
      
          @Override
          public void move() {
              System.out.println("Uses it's limbs");
      
          }
      
          @Override
          public void sleep() {
              System.out.println("Sleeps on it's tummy");
      
          }
      
      }
      
      
      public class Snake extends Reptile{
      
          @Override
          public void move() {
              System.out.println("Crawls");
      
          }
      
          @Override
          public void sleep() {
              System.out.println("");
      
          }
      
      }
      

      正如您在此示例中所看到的,Dog 扩展了 MammalSnake 扩展了 Reptile。因此,在任何给定时间点,Dog 将永远是 MammalSnake 将永远是 Reptile。因此,这种is-a 关系在两个实体之间建立了strong bond。在执行过程中,只要Dog扩展Mammal,就不能是Reptile或其他任何东西。

      回到 has-a 关系(组合),我可以说 Person has-a Car。我没有指定此人拥有的Car 的类型。让我们回到面向对象的角度来更好地理解这一点。

      考虑这个Person

      public class Person {
          private String name;
          private String address;
      
          private Car car;
      
          public Person(String name, String address, Car car) {
              super();
              this.name = name;
              this.address = address;
              this.car = car;
          }
      
      
      }
      

      这里可以看出,一个Person有一个名字、地址和汽车。 Car 和相关类的代码如下:

      public class Car {
          //Car related methods
      }
      
      public class Ferrari extends Car{
          //Ferrari specific methods, attributes
      }
      
      public class Bmw extends Car{
          //BMW specific attributes, methods.
      }
      

      可以看出BMWFerrariCar的类型。 现在回到声明“Person has-a Car”,Car 可以是任何 Car。它可能是法拉利或宝马或其他任何东西。这可以在创建Person 对象时传入。

      Person john = new Person("John", "#81, 2nd Street,..", new Ferrari());

      可以看出,John 接受了一个Ferrari Car。在这里,John 可以接收在对象创建期间传递的任何类型的Car(动态性质)。因此,这种级别的灵活性是由 has-a(组合)关系提供的。

      我希望这能消除您的疑虑。

      【讨论】:

        猜你喜欢
        • 2013-01-03
        • 2015-09-16
        • 2010-12-02
        • 1970-01-01
        • 1970-01-01
        • 2016-10-02
        • 2012-05-26
        • 2012-11-01
        • 1970-01-01
        相关资源
        最近更新 更多