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

我在设计模式中一次又一次地听到过这一点。为此引用的一些原因是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初学者来说,在这些关键点上通过示例会很棒。

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


答案 1

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

继承的利弊Favor Composition over Inheritance

优势:

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

弊:

  • 中断封装,因为它向其超类的实现
    详细信息公开子类。
  • White-box重用,因为超类的内部细节通常对子类可见。
  • 如果超类的实现发生更改,则可能必须更改子类。从超类继承的实现不能在运行时更改。

关于问题:

继承是强耦合的,其中作为组合是松散耦合的

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

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

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

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

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

继承破坏了封装,而作为组合则没有

是的。现在,您可以看到继承的缺点。

底线是

确保继承模型是一种关系我的主要指导思想是,只有当子类超类时,才应使用继承。在上面的例子中,一个可能的是-a,所以我倾向于使用继承。AppleFruit

当你认为自己有一个 is-a 关系时,要问自己的一个重要问题是,这种关系是否在应用程序的整个生命周期中都是恒定的,幸运的是,在代码的整个生命周期中也是如此。例如,您可能认为 is-a ,当真正表示 a 在部分时间中扮演的角色。如果这个人失业了怎么办?如果这个人既是一个又是一个?这种无常的 is-a 关系通常应该用组合来建模。EmployeePersonEmployeePersonEmployeeSupervisor

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

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

支持组合而不是继承:)

我直接从java世界中拿走了它。


答案 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(); }
}

在这种情况下,我可以通过传递接口的不同实现来更改 的行为。BarFoo

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


推荐