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 |
修改后,不再正常:
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警告,不要再使用方法,同时为他们提供一个窗口,在该窗口中修复他们的代码以使用替换方法/替代方法。