oop - 有什么组合不能实现继承可以吗?

标签 oop inheritance polymorphism composition

组合和继承。

我知道它们都是在适当时选择的工具,并且上下文对于在组合和继承之间进行选择非常重要。然而,关于每种情况的适当上下文的讨论通常有点模糊。这让我开始考虑继承和多态是传统 OOP 的不同方面有多么明显。

多态性允许人们平等地指定“is-a”关系以及继承。特别是,从基类继承隐含地在该类与其子类之间创建了多态关系。然而,虽然多态可以使用纯接口(interface)来实现,但继承通过同时传递实现细节使多态关系复杂化。通过这种方式,继承与纯多态完全不同。

作为一种工具,继承通过简化实现重用为程序员提供不同于多态性(通过纯接口(interface))的服务。在琐碎的情况下 .然而,在大多数情况下,父类(super class)的实现细节与子类的要求存在微妙的冲突。这就是我们有“覆盖”和“成员隐藏”的原因。在这些情况下,继承提供的实现重用是通过验证状态更改和跨级联代码级别的执行路径的额外努力来购买的:子类的完整“扁平化”实现细节分布在多个类之间,这通常意味着多个文件,其中只有部分适用于所讨论的子类。在处理继承时,查看该层次结构是绝对必要的,因为如果不查看父类(super class)的代码,就无法知道哪些未覆盖的细节会影响您的状态或转移您的执行。

相比之下,组合的独占使用保证您将看到哪些状态可以被显式实例化的对象修改,这些对象的方法由您自行决定调用。仍然没有实现真正扁平化的实现(实际上甚至是不可取的,因为结构化编程的好处是实现细节的封装和抽象)但是您仍然可以重用代码,并且您只需要查看一个地方当代码行为不端时。

为了在实践中测试这些想法,避免传统的继承以结合纯基于接口(interface)的多态性和对象组合,我想知道,

有什么对象组合和接口(interface)不能实现继承可以吗?

编辑

在迄今为止的答复中,ewernli 认为没有一种技术可以用于技术专长,而另一种则没有。他后来提到了每种技术所固有的不同模式和设计方法。这是有道理的。但是,该建议使我通过询问排他使用组合和接口(interface)来代替传统继承是否会禁止使用任何主要设计模式来完善我的问题?如果是这样,在我的情况下没有等效的模式吗?

最佳答案

从技术上讲,所有可以通过继承实现的东西也可以通过委托(delegate)来实现。所以答案是“不”。

将继承转化为委托(delegate)

假设我们有以下通过继承实现的类:

public class A {
    String a = "A";
    void doSomething() { .... }
    void getDisplayName() {  return a }
    void printName { System.out.println( this.getDisplayName() };   
}

public class B extends A {
    String b = "B";
    void getDisplayName() {  return a + " " + b; }
    void doSomething() { super.doSomething() ; ... }    
}

这些东西很好用,调用printName在 B 的实例上将打印 "A B"在控制台中。

现在,如果我们用委托(delegate)重写它,我们得到:
public class A {
    String a = "A";
    void doSomething() { .... }
    void getDisplayName() {  return a }
    void printName { System.out.println( this.getName() };  
}

public class B  {
    String b = "B";
    A delegate = new A();
    void getDisplayName() {  return delegate.a + " " + b; }
    void doSomething() { delegate.doSomething() ; ... } 
    void printName() { delegate.printName() ; ... }
}

我们需要定义printName在 B 中,并在实例化 B 时创建委托(delegate)。调用 doSomething将以与继承类似的方式工作。但是调用printName将打印 "A"在控制台中。实际上,通过委托(delegate),我们失去了“this”绑定(bind)到对象实例和能够调用已被覆盖的方法的基本方法的强大概念。

这可以在支持纯委托(delegate)的语言中解决。对于纯委托(delegate),委托(delegate)中的“this”仍将引用 B 的实例。这意味着 this.getName() will 启动类 B 的方法分派(dispatch)。我们实现与继承相同的方法。这是 prototype-based 中使用的机制语言如 Self具有委托(delegate)功能的内置功能(您可以阅读 here Self 中的继承如何工作)。

但是Java没有纯委托(delegate)。什么时候卡住了?不,真的,我们仍然可以自己做更多的努力:
public class A implements AInterface {
    String a = "A";
    AInterface owner; // replace "this"
    A ( AInterface o ) { owner = o }
    void doSomething() { .... }
    void getDisplayName() {  return a }
    void printName { System.out.println( owner.getName() }; 
}

public class B  implements AInterface {
    String b = "B";
    A delegate = new A( this );
    void getDisplayName() {  return delegate.a + " " + b; }
    void doSomething() { delegate.doSomething() ; ... } 
    void printName() { delegate.printName() ; ... }
}

我们基本上是在重新实现内置继承提供的功能。是否有意义?不完全是。但它说明继承总是可以转换为委托(delegate)。

讨论

继承的特点是基类可以调用在子类中被覆盖的方法。例如,这就是 template pattern 的本质。 .这样的事情不能通过委派轻松完成。另一方面,这正是使继承难以使用的原因。理解多态调度发生在哪里以及如果方法被覆盖会产生什么影响,需要精神上的扭曲。

有一些关于继承的已知陷阱以及它可能在设计中引入的脆弱性。特别是如果类层次结构不断发展。 equality 也可能存在一些问题在 hashCodeequals如果使用继承。但另一方面,它仍然是解决一些问题的一种非常优雅的方式。

此外,即使继承可以用委托(delegate)代替,您也可以争辩说它们仍然实现不同的目的并相互补充——它们没有传达相同的意图,这不是纯粹的技术等效性所捕捉到的。

(我的理论是,当有人开始做 OO 时,我们很想过度使用继承,因为它被认为是语言的一个特性。然后我们学习委托(delegate),这是模式/方法,我们也学会了喜欢它。一段时间后,我们在两者之间找到了平衡,并培养了直觉,在哪种情况下哪个更好。好吧,正如你所看到的,我仍然喜欢两者,并且在介绍之前都值得谨慎。)

部分文献
  • Delegation is inheritance

  • Inheritance and delegation are alternate methods for incremental definition and sharing. It has commonly been believed that delegation provides a more powerful model. This paper demonstrates that there is a “natural” model of inheritance which captures all of the properties of delegation. Independently, certain constraints on the ability of delegation to capture inheritance are demonstrated. Finally, a new framework which fully captures both delegation and inheritance is outlined, and some of the ramifications of this hybrid model are explored.


  • On the notion of inheritance

  • One of the most intriguing—and at the same time most problematic—notions in object-oriented programing is inheritance. Inheritance is commonly regarded as the feature that distinguishes object-oriented programming from other modern programming paradigms, but researchers rarely agree on its meaning and usage. [...]


  • Systematically refactoring inheritance to delegation in java

  • Because of the strong coupling of classes and the proliferation of unneeded class members induced by inheritance, the suggestion to use composition and delegation instead has become commonplace. The presentation of a corresponding refactoring in the literature may lead one to believe that such a transformation is a straightforward undertaking. [...]

    关于oop - 有什么组合不能实现继承可以吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2238642/

    相关文章:

    java - 如何在一些子类之间共享代码?

    java - 访问运行时设置的变量值

    c# - 泛型方法继承 : VS and Xamarin Studio yield different results; which is right?

    java - 对子类的对象使用父类(super class)的相同方法

    java - List<Dog> 是 List<Animal> 的子类吗?为什么 Java 泛型不是隐式多态的?

    python - 下面的代码在Python3中实现了多态性,这样说是否正确?

    php - PHP 中的日期比较

    c++ - 我们什么时候应该在 unique_ptr 中使用自定义删除器而不是默认删除器?

    C# 继承构造函数子和父 ??

    c++ - 从模板基类派生时找不到类型