我在设计模式中一次又一次地听到这个favor composition over inheritance
。引用的一些原因是
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
对于像我这样的初学者来说,通过插图来理解遗传和构图在上述方面的不同之处会很棒。我已经阅读了各种关于它们的SO链接,但是通过这些关键点的示例对Java初学者来说非常有用。
我认为清楚地理解这些差异非常重要,而不仅仅是记住要点。
答案 0 :(得分:7)
对于初学者来说这是个好问题,我想我应该首先提醒读者什么是遗产和构成,然后继续解释Favor Composition over Inheritance
的确切含义。
继承的利弊:
<强>优点:强>
<强>缺点:强>
White-box
重用,因为超类的内部细节经常是
对子类可见。关于这个问题:
在组合松散耦合的情况下,继承是强耦合的
继承将为您带来紧密耦合,只需对基类进行一次更改就可以破坏许多子类。
但何时使用以及如何检测我们需要继承或组合?
仅在满足以下所有条件时使用继承(Coad规则):
is a special kind of
而不是is a role played by a
。继承是编译时确定的,其中组合是运行时
编译时,您的基类代码将添加到每个子类中。
继承打破封装,而组合不是
是。现在你看到了继承的缺点。
底线是:
确保继承模拟is-a关系
我的主要指导思想是只有当子类是-a 超类时才应该使用继承。在上面的示例中,Apple
可能是-a Fruit
,因此我倾向于使用继承。
当你认为自己有一个is-a关系时,问一个自己的一个重要问题是,这是一个关系在整个应用程序的生命周期中是不变的,幸运的是,代码的生命周期。例如,您可能认为Employee
是Person
,而真正Employee
代表Person
扮演部分时间的角色。如果这个人失业怎么办?如果此人同时是Employee
和Supervisor
,该怎么办?这种无常的关系通常应该用构图来建模。
不要仅使用继承来重用代码 如果您真正想要的只是重用代码并且看不到任何关系,请使用合成。
不要仅仅为了获得多态性而使用继承
如果您真正想要的只是多态,但没有自然的关系,请使用带接口的合成。
赞成过度继承:)
我直接从javaworld。
答案 1 :(得分:2)
继承在代码中使用extends表示具体类。一旦你编写它,你就无法在不重写类的情况下进行更改。我只能通过修改代码来改变。
public class Foo {
public void doSomething() { System.out.println("I'm a Foo"); }
}
public class Bar extends Foo {
public void doSomething() { super.doSomething(); }
}
我只能通过修改一个或两个类来改变Bar
的作用。
组合通常是基于接口的,这意味着您指定了要做的事情,而不是如何做。只需更改接口的实现,即可在不影响客户端的情况下更改方式。
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(); }
}
在这种情况下,我可以通过传递Bar
接口的不同实现来更改Foo
的行为。
这是Bob Martin的SOLID原则之一:The Open/Closed Principle。