关于设计模式:为什么继承在Java中松散耦合的情况下是强耦合的?

why inheritance is strongly coupled where as composition is loosely coupled in Java?

本问题已经有最佳答案,请猛点这里访问。

我在设计模式中一次又一次地听到这个favor composition over inheritance。其中一些原因是

1
2
3
4
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

对于像我这样的初学者来说,通过举例说明继承和构图在上述几点上的不同,这将是非常好的。我已经阅读了很多关于它们的链接,但是通过这些关键点的例子对Java初学者来说是很好的。

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


对于初学者来说,这个问题很好,也很重要,我认为我应该首先提醒读者什么是继承和组合,然后继续解释Favor Composition over Inheritance的确切含义。继承的利弊:

优势:

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

缺点:

  • 中断封装,因为它向实现公开子类超级级的细节。
  • 由于超级类的内部细节通常是子类可见。
  • 如果超类更改。从超类继承的实现可以运行时不更改。

关于问题:

Inheritance is strongly coupled where as composition is loosely coupled

继承将给您带来紧密耦合,只需对基类进行一次更改就可以破坏许多子类。但何时使用以及如何检测我们需要继承或组合?仅当满足以下所有条件时才使用继承(COAD规则):

  • 一个子类表达is a special kind of,而不是is a role played by a
  • 子类的实例永远不需要成为另一个类的对象。
  • 子类扩展,而不是覆盖或取消它是一流的。
  • 子类不扩展实用工具类。
  • 对于实际问题域中的类,子类角色、事务或设备。
  • Inheritance is compile time determined where as composition is run-time

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

    Inheritance breaks encapsulation where as composition does not

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

    底线是:

    确保继承为IS-A关系建模我的主要指导思想是继承只能在子类是超类时使用。在上面的例子中,Apple可能是Fruit,所以我倾向于使用继承。

    当您认为您拥有IS-A关系时,需要问自己的一个重要问题是,这种关系是否会在应用程序的整个生命周期中保持不变,如果幸运的话,还会在代码的生命周期中保持不变。例如,你可能认为一个Employee是一个Person,而实际上Employee代表一个Person在时间中扮演的角色。如果这个人失业了怎么办?如果此人同时是EmployeeSupervisor呢?这种无常的IS-A关系通常应该用组合来建模。

    不要使用继承来获得代码重用如果您真正想要的只是重用代码,而不存在所谓的关系,那么就使用组合。

    不要使用继承来获取多态性如果您真正想要的只是多态性,但没有自然的IS-A关系,那么就使用组合和接口。有利于组合而非继承:)

    我直接从爪哇世界拿的。


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

    1
    2
    3
    4
    5
    6
    7
    public class Foo {
        public void doSomething() { System.out.println("I'm a Foo"); }
    }

    public class Bar extends Foo {
        public void doSomething() { super.doSomething(); }
    }

    我只能通过修改一个或两个类来更改Bar所做的操作。

    组合通常是基于接口的,这意味着您要指定要做什么,而不是如何做。只需更改接口的实现,就可以在不影响客户端的情况下更改方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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的行为。

    这是鲍勃·马丁的坚实原则之一:开放/封闭原则。