Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/29.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 有什么东西是合成不能完成的,继承可以完成的吗?_Oop_Inheritance_Polymorphism_Composition - Fatal编程技术网

Oop 有什么东西是合成不能完成的,继承可以完成的吗?

Oop 有什么东西是合成不能完成的,继承可以完成的吗?,oop,inheritance,polymorphism,composition,Oop,Inheritance,Polymorphism,Composition,构成和继承 我知道它们都是在适当的时候选择的工具,而上下文在组合和继承之间的选择是非常重要的。然而,关于每种语言的适当语境的讨论通常有点模糊;这让我开始思考,如何明确继承和多态是传统OOP的独立方面。 多态性允许人们平等地指定“is-a”关系以及继承。特别是,从基类继承会隐式地在该类及其子类之间创建多态关系。然而,尽管多态性可以使用纯接口实现,但继承通过同时传输实现细节使多态关系复杂化。通过这种方式,继承与纯多态性是完全不同的 作为一种工具,继承为程序员提供的服务不同于多态性(通过纯接口),它简

构成和继承

我知道它们都是在适当的时候选择的工具,而上下文在组合和继承之间的选择是非常重要的。然而,关于每种语言的适当语境的讨论通常有点模糊;这让我开始思考,如何明确继承和多态是传统OOP的独立方面。 多态性允许人们平等地指定“is-a”关系以及继承。特别是,从基类继承会隐式地在该类及其子类之间创建多态关系。然而,尽管多态性可以使用纯接口实现,但继承通过同时传输实现细节使多态关系复杂化。通过这种方式,继承与纯多态性是完全不同的

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

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

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

有什么对象组合和接口不能完成继承所能完成的吗

编辑


在到目前为止的回复中,ewernli认为一种技术没有技术专长,但另一种技术没有;他后来提到了每种技术固有的不同模式和设计方法。这是理所当然的。然而,这个建议让我进一步完善了我的问题,我问:排他性地使用组合和接口来代替传统继承是否会禁止使用任何主要的设计模式?如果是这样的话,在我的情况下,难道没有等效的模式可供使用吗?

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

将继承转换为委托

假设我们使用继承实现了以下类:

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() ; ... }    
}
这些东西工作得很好,对B实例调用
printName
将在控制台中打印
“ab”

现在,如果我们用授权重写它,我们得到:

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() ; ... }
}
我们需要在B中定义
printName
,还需要在实例化B时创建委托。调用
doSomething
将以与继承类似的方式工作。但是调用
printName
将在控制台中打印
“a”
。事实上,通过委托,我们失去了“this”绑定到对象实例的强大概念,而基方法能够调用已被重写的方法

这可以通过支持纯委托的语言来解决。对于纯委托,委托中的“this”仍将引用B的实例。这意味着
this.getName()
将从类B开始方法分派。我们实现了与继承相同的效果。这是语言中使用的机制,例如have delegation具有内置特性的语言(您可以阅读Self中继承是如何工作的)

但是Java没有纯粹的委托。什么时候会卡住?不,真的,我们还可以自己再努力一点:

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() ; ... }
}
我们基本上是在重新实现内置继承提供的功能。这有意义吗?不,真的。但它说明了继承总是可以转换为委托

讨论

继承的特点是基类可以调用在子类中重写的方法。例如,这就是该计划的实质。这样的事情授权是不容易做到的。另一方面,这正是使继承难以使用的原因。要理解多态分派发生在何处,以及重写方法会产生什么影响,这需要一种思维扭曲

关于继承以及它可能在设计中引入的脆弱性,存在一些已知的陷阱。尤其是在类层次结构发生变化的情况下。如果使用继承,in
hashCode
equals
也可能存在一些问题。但另一方面,这仍然是解决一些问题的一种非常优雅的方式

此外,即使继承可以用委托来代替,你也可以说它们仍然达到不同的目的,并相互补充——它们并没有传达出纯技术等价所没有的相同意图

(我的理论是,当有人开始做OO时,我们是临时工
Widget baseWidget = new Widget();
CustomWidget customWidget = new CustomWidget();
baseWidget.addChildWidget(customWidget);
Widget baseWidget = new Widget();
CustomWidget customWidget = new CustomWidget();
customWidget.addAsChildToWidget(baseWidget);