组合优于Java继承

时间:2016-12-08 13:34:42

标签: java

以下段落引自本书,#34; Thinking in Java"布鲁斯·埃克尔(Bruce Eckel)。

  

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

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

3 个答案:

答案 0 :(得分:9)

如果您的课程延长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>();
        }
    }
}

答案 1 :(得分:2)

如果你使用继承,你很难看到整个画面,你必须学习很多代码才能理解发生了什么。如果你改变父类中的一个小东西,你就有麻烦了。所有的后代呢?这种变化对他们有影响吗?

还要考虑测试。父类不能分开,你必须测试一切,测试很复杂,通常根本没有,因为努力太大,无法使测试有意义。

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

继承也很危险。例如,当您想要计算时,您向HashSet添加了多少项。您可以覆盖方法add和addAll,但是您不知道,如果addAll调用方法为每个项添加。见example

要回答原始问题 - 您可以通过构造函数或setter传递内部对象。

答案 2 :(得分:2)

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

当我说,Dog MammalSnake Reptile,但两者都是{ {1}},设计可以是这样的,应该有一个名为Animals的超类和两个名为AnimalMammal的子类ReptileDog应该扩展。相应代码如下:

Snake

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

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(""); } } 扩展了DogMammal扩展了Snake。因此,在任何给定的时间点,Reptile 始终一个DogMammal 始终一个{{1 }}。因此,是一个关系会在两个实体之间建立一个强大的债券。在执行过程中,只要Snake延伸Reptile,就不能是Dog或其他任何内容。

回到 has-a 关系(撰写),我可以说Mammal 有一个 Reptile。我没有指定此人拥有的Person的类型。让我们回到OO的角度来更好地理解这一点。

考虑这个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;
    }


}

可以看出Personpublic class Car { //Car related methods } public class Ferrari extends Car{ //Ferrari specific methods, attributes } public class Bmw extends Car{ //BMW specific attributes, methods. } BMW的类型。 现在回到声明&#34;人有一辆汽车&#34; ,汽车可以是任何汽车。它可能是法拉利或宝马或其他任何东西。这可以在创建Ferrari对象期间传递。

Car

可以看出约翰接受了Person Person john = new Person("John", "#81, 2nd Street,..", new Ferrari());。在这里John可以接受在对象创建期间传递的任何类型的Ferrari(动态性质)。因此,灵活性水平由 has-a (组合)关系提供。

我希望这可以解除你的怀疑。