为什么继承是强耦合的,因为组合在Java中松散耦合?

时间:2013-10-02 21:16:18

标签: java design-patterns inheritance composition

我在设计模式中一次又一次地听到这个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初学者来说非常有用。

我认为清楚地理解这些差异非常重要,而不仅仅是记住要点。

2 个答案:

答案 0 :(得分:7)

对于初学者来说这是个好问题,我想我应该首先提醒读者什么是遗产和构成,然后继续解释Favor Composition over Inheritance的确切含义。

继承的利弊

<强>优点:

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

<强>缺点:

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

关于这个问题:

  

在组合松散耦合的情况下,继承是强耦合的

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

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

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

    继承是编译时确定的,其中组合是运行时

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

      

    继承打破封装,而组合不是

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

    底线是

    确保继承模拟is-a关系 我的主要指导思想是只有当子类是-a 超类时才应该使用继承。在上面的示例中,Apple可能是-a Fruit,因此我倾向于使用继承。

    当你认为自己有一个is-a关系时,问一个自己的一个重要问题是,这是一个关系在整个应用程序的生命周期中是不变的,幸运的是,代码的生命周期。例如,您可能认为EmployeePerson,而真正Employee代表Person扮演部分时间的角色。如果这个人失业怎么办?如果此人同时是EmployeeSupervisor,该怎么办?这种无常的关系通常应该用构图来建模。

    不要仅使用继承来重用代码   如果您真正想要的只是重用代码并且看不到任何关系,请使用合成。

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

    赞成过度继承:)

    我直接从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