为什么在Java中继承是强耦合的,而组合是松耦合的?
为什么在Java中继承是强耦合的,而组合是松耦合的?
这个问题已经有了答案:
我在设计模式中一遍又一遍听到了这句话:倾向于使用组合而不是继承
。一些原因被引用:
1)Inheritance is strongly coupled where as composition is loosely coupled 2) Inheritance is compile time determined where as composition is run-time 3)Inheritance breaks encapsulation where as composition does not 4) anything else I am not aware of
对于像我这样的初学者来说,通过实例说明继承和组合在以上几个点上的区别会非常有帮助。我已经阅读了各种相关的Stack Overflow主题,但是关于这些关键点的例子还是对Java初学者来说非常有帮助的。
我认为,要清楚地理解它们之间的区别比仅仅记住这些观点更为重要。
继承可以通过具体类使用扩展来表达。一旦编写了它,就不能在不重写类的情况下更改它。只能通过修改代码来进行更改。
public class Foo { public void doSomething() { System.out.println("I'm a Foo"); } } public class Bar extends Foo { public void doSomething() { super.doSomething(); } }
组合通常是基于接口的,这意味着您可以指定要执行的操作,而不是如何执行。您可以通过更改接口的实现而不影响客户端来更改如何执行。
public interface Foo { void doSomething(); } public class FooImpl implements Foo { public void doSomething() { System.out.println("I'm a Foo"); } } public class Bar implements Foo { private Foo foo; public Bar(Foo f) { this.foo = f; } public void doSomething() { this.foo.doSomething(); } }
在这种情况下,我可以通过传递不同的Foo
接口实现来简单地更改Bar
的行为。
这是一个不错的问题,对于初学者来说,有些大。我认为首先应该提醒读者继承和组合是什么,然后解释一下具体什么是“喜欢组合多于继承”。
继承的优缺点:
优点:
- 动态绑定和多态的主要好处之一是能够帮助代码更容易修改。
- 新的实现很容易,因为它们大部分是继承的。重用的实现很容易修改或扩展。
缺点:
- 破坏了封装性,因为它使子类暴露于其超类的实现细节之中。
白盒子
重用,因为超类的内部细节通常对子类可见。- 如果超类的实现更改,可能需要更改子类。从超类继承的实现无法在运行时更改。
关于问题:
继承是强耦合的,而组合是松耦合的
继承会带来紧密耦合,简单来说,对基类的一次更改可能会破坏许多子类。
但是何时使用并如何检测我们需要继承还是组合?
只有当满足以下所有标准(Coad's Rule)时才使用继承:
- 子类表达“是一种特殊类型”,而不是“是由某个角色扮演”的关系。
- 子类的实例永远不需要成为另一个类的对象。
- 子类扩展了超类的职责,而不是重写或使其无效。
- 子类不扩展仅仅是一个效用类的功能。
- 对于实际问题领域的类,子类专门化了一个角色、事务或设备。
继承是在编译时确定的,而组合是在运行时确定的
在编译时,基类的代码将添加到每个子类中。
继承破坏封装性,而组合不会破坏封装性
是的。现在你看到了继承的缺点。
关键是:
确保继承建模is-a关系
我的主要指导理念是只有当子类是超类时,继承才应该被使用。例如,Apple
很可能是Fruit
,所以我会倾向于使用继承。
当你认为你有一个is-a关系时,问自己一个重要的问题,即这个is-a关系在应用程序的整个生命周期以及代码的生命周期中是否始终不变。例如,你可能认为Employee
是一个Person
,但实际上,Employee
代表一个Person
在一段时间内扮演的角色。如果这个人失业了怎么办?如果这个人既是一个Employee
又是一个Supervisor
呢?这种不固定的is-a关系通常应该用组合来建模。
不要仅仅使用继承来获得代码复用
如果你真正想要的只是重用代码,而没有任何is-a关系的话,请使用组合。
不要仅仅使用继承来获得多态
如果你真正想要的是多态性,但是没有自然的is-a关系,请使用带有接口的组合。
比起继承,优先使用组合:)
我从javaworld上直接引用的。