关于java:继承与组合?

Inheritance vs Composition?

一个月前,我问了类似的问题:通过组合继承?

我从本文中举了一些例子:http://www.javaworld.com/javaworld/jw-11-1998/jw-11-technologies.html?页面=1然而,这次是另一个问题。很抱歉发布了很多代码,但它很长,不难阅读。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Fruit {
    public int peel() {

            System.out.println("Peeling is appealing.");
            return 1;
        }
    }

class Apple extends Fruit {
}

class Example1 {

    public static void main(String[] args) {

        Apple apple = new Apple();
        int pieces = apple.peel();
    }
}

修改后,不再正常:

1
2
3
4
5
6
7
8
9
10
class Peel {

    private int peelCount;  
    public Peel(int peelCount) {
        this.peelCount = peelCount;
    }    
    public int getPeelCount() {    
        return peelCount;
    }
}

修改类水果会导致编译错误代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Peel {

    private int peelCount;

    public Peel(int peelCount) {
        this.peelCount = peelCount;
    }

    public int getPeelCount() {

        return peelCount;
    }
}

class Fruit {

// Return a Peel object that
// results from the peeling activity.
    public Peel peel() {

        System.out.println("Peeling is appealing.");
        return new Peel(1);
    }
}

旧的实现被破坏。这个问题可以通过合成来解决:

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
class Peel {

    private int peelCount;

    public Peel(int peelCount) {
        this.peelCount = peelCount;
    }

    public int getPeelCount() {

        return peelCount;
    }
}


class Fruit {

// Return int number of pieces of peel that
// resulted from the peeling activity.
public Peel peel() {

System.out.println("Peeling is appealing.");
        return new Peel(1);
    }
}

// Apple must be changed to accomodate
// the change to Fruit
class Apple {

    private Fruit fruit = new Fruit();

    public int peel() {

        Peel peel = fruit.peel();
        return peel.getPeelCount();
    }
}

// This old implementation of Example2
// still works fine.
class Example1 {

    public static void main(String[] args) {

        Apple apple = new Apple();
        int pieces = apple.peel();
    }
}

随着作曲风格的改变,苹果不再是一种水果关系了。它变成了一个has-a.水果的方法被委托给苹果作为继承。我的问题出现在这里:

  • 使用包装方法从水果中委托方法,这不是促进复制和粘贴吗,这是一个坏做法?如果我们有大约10-50种水果方法。如果要继承的子类大约有50个,那么如何通过组合继承呢?

  • 关于第一个例子,使用扩展继承,作者建议在超类中进行一次更改将破坏使用它的实现。然而,我想知道,为什么需要改变它?OO的原则之一不是"关闭进行修改,打开进行扩展"?在作者所说的情况下,我仍然可以保留旧的超类实现,并使它适应新的变化。这样地:

    等级水果{水果}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // Return a Peel object that
    // results from the peeling activity.
    private Peel getPeel() {

        System.out.println("Peeling is appealing.");
        return new Peel(1);
    }
    public int peel(){
        Peel peel = getPeel();
        return peel.getPeelCount();
    }

    }

在超类中添加新方法以适应而不是更改或替换旧方法是否有问题,这将破坏程序结构?据我所见,通过这样做,我仍然可以实现与组成示例相同的事情,而且我不必为每个子类委托超类的方法而制作很多包装器方法。

  • 基于以上原因,我得出结论:当存在多个继承时,或者当我需要分享不同且不属于一个阶级家庭的行为时,使用作文的唯一时间更好。例如:

界面健谈{

1
 public void talk();

}

动物类家庭既可以实现健谈的界面,也可以实现人类家庭。你觉得我的结论怎么样?


基本上,您似乎试图通过创建包装类来避免更改依赖于旧API的客户机。

不幸的是,这不太管用。它导致包装类和其他创建包装类实例的困难的扩散。如果您这样做,您的代码库/应用程序就会膨胀,性能会受到影响,代码也会变得越来越难以理解。

更好的方法是:

  • 首先尝试正确使用API。(杜!)
  • 如果API的更改没有增加重要的价值或修复一些重要的东西,请推迟。
  • 当您确实需要对API进行更改时,请以二进制兼容的方式进行更改;例如,添加新方法,而不是对现有方法进行二进制不兼容的更改。如果打算去掉旧方法,请将其标记为已弃用。
  • 当您必须更改二进制不兼容的API时,请同时更改所有使用它的代码。

@deprecated标记允许您向人们发出API警告,不要再使用方法,同时为他们提供一个窗口,在该窗口中修复他们的代码以使用替换方法/替代方法。