12

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 初学者来说非常有用。

我认为清楚地理解差异比仅仅记住要点非常重要。

4

2 回答 2

8

很好的问题,对初学者来说很大,我想我应该首先提醒读者继承和组合是什么,然后继续解释究竟是什么Favor Composition over Inheritance意思。

继承的优点和缺点

优点:

  • 动态绑定和多态性的主要好处之一是它们可以帮助使代码更容易更改。
  • 新的实现很容易,因为大部分都是继承的。很容易修改或扩展被重用的实现。

缺点:


  • 打破封装,因为它将子类暴露给其超类的实现细节。
  • White-box重用,因为超类的内部细节通常对子类可见。
  • 如果超类的实现发生变化,则可能必须更改子类。从超类继承的实现不能在运行时更改。

关于问题:

继承是强耦合的,而组合是松耦合的

继承会给你带来紧密的耦合,只需对基类进行一次更改就可以破坏许多子类。

但是何时使用以及如何检测我们需要继承或组合?
仅当满足以下所有条件时才使用继承(Coad 规则):

  1. 子类表示is a special kind of而不是is a role played by a
  2. 子类的实例永远不需要成为另一个类的对象。
  3. 子类扩展而不是覆盖或取消其超类的职责。
  4. 子类不会扩展仅仅是实用程序类的功能。
  5. 对于实际问题域中的类,子类专门用于角色、事务或设备。

继承是编译时间决定的,而组合是运行时间

编译后,您的基类代码将添加到每个子类中。

继承打破了封装,而组合没有

是的。现在你看到了继承的缺点。

底线是

确保继承对 is-a 关系建模我的主要指导理念是,仅当子类超类 时才应使用继承。在上面的示例中,Apple可能是 is-a Fruit,因此我倾向于使用继承。

当你认为你有一个 is-a 关系时,要问自己一个重要的问题是,这个 is-a 关系是否会在应用程序的整个生命周期以及代码的生命周期中保持不变。例如,您可能认为Employeeis-aPerson真正Employee代表了 aPerson在时间中扮演的角色。如果这个人失业了怎么办?如果这个人既是 aEmployee又是 aSupervisor怎么办?这种无常的 is-a 关系通常应该用组合来建模。

不要仅仅为了代码重用而使用继承 如果您真正想要的只是重用代码并且看不到 is-a 关系,请使用组合。

不要仅仅为了获得多态性而使用继承 如果您真正想要的只是多态性,但没有自然的 is-a 关系,请使用带有接口的组合。

偏好组合胜过继承 :)

我直接从javaworld 拿来的。

于 2013-10-02T23:20:21.450 回答
2

继承在代码中使用具体类的扩展来表示。一旦你写了它,你就不能在不重写类的情况下改变它。我只能通过修改代码来改变。

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原则之一:开放/封闭原则

于 2013-10-02T21:18:50.613 回答