Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.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_Design Patterns_Inheritance_Language Agnostic_Composition - Fatal编程技术网

Oop 继承能被合成完全取代吗?

Oop 继承能被合成完全取代吗?,oop,design-patterns,inheritance,language-agnostic,composition,Oop,Design Patterns,Inheritance,Language Agnostic,Composition,这个问题是而不是像“继承与构成”这样的问题 我完全理解继承与组合的区别,我知道它们的优点和缺点,而且这两个概念似乎都很简单。但是关于继承和组合的问题太多了,我想,也许我误解了这个简单的想法 让我们集中精力。Go是一种来自Google的语言,每个人都很兴奋它没有继承,没有类,但它有组合,这很酷。 对我来说,Go中的组合为您提供了与其他语言(C++,Java,…)中的继承完全相同的功能。组件方法自动公开,并作为后续结构的方法提供,如下所示: 主程序包 进口( “fmt” ) 类型汽车结构{ 名称字符

这个问题是而不是像“继承与构成”这样的问题

我完全理解继承与组合的区别,我知道它们的优点和缺点,而且这两个概念似乎都很简单。但是关于继承和组合的问题太多了,我想,也许我误解了这个简单的想法

让我们集中精力。Go是一种来自Google的语言,每个人都很兴奋它没有继承,没有类,但它有组合,这很酷。 对我来说,Go中的组合为您提供了与其他语言(C++,Java,…)中的继承完全相同的功能。组件方法自动公开,并作为后续结构的方法提供,如下所示:

主程序包
进口(
“fmt”
)
类型汽车结构{
名称字符串
}
func(c*Car)move()bool{
返回真值
} 
类型MyCar结构{
汽车
}
func main(){
var c MyCar
fmt.Print(c.move())
}
综上所述,组合比继承更好,因为:

  • 更灵活(允许您在运行时更改组件,因此可以影响“类”的工作方式)
  • 没有钻石问题(但钻石问题是可以解决的,所以这不是很强的优势)
  • <强>并且如果你考虑Go及其接口(每个对象,都有一个接口定义的方法,实现这个接口含义),你有最终的解决方案吗? 我们是否可以说,含有一些语法糖分的组合可以取代继承?

    这样的设计符合Liskov替换原则。我是否错过了一些东西,或者继承(从任何语言中都知道)与从Go中知道的组合(和接口)相比没有优势

    ====edit1=====

    为了澄清,可以在
    Go
    中使用“标准”合成机制,如下所示(此示例的行为与前一示例类似):

    但是,如果您像在上一个示例中那样使用它,那么所有的方法都可以隐式地“在MyCar类中”使用。

    简短的回答 它真的没有那么黑和白。简言之,是的。任何可以通过继承解决的情况都可以通过组合来解决。所以简言之,你的问题的答案是肯定的;继承可以被组合所取代

    为什么没那么简单 何时使用继承
    这不是你是否可以交换它们的问题。这取决于你在其中编程的上下文,而更多的是你是否应该交换它们的问题。以Java中的这个简单示例为例:

    public class Person
    {
        // Assume every person can speak.
        public void speak()
        {
        }
    }
    
    现在,假设我们有另一门课,戴夫。戴夫是一个人

    public class Dave extends Person
    {
         public void speak() { System.out.println("Hello!"); }
         public void killSomeone() {} // Dave is a violent Guy.
    }
    
    现在让类
    Dave
    看起来像这样更有意义吗

    public class Dave
    {
         private Person p;
         // Composition variant.
    
         public void speak() { p.speak(); }
         public void killSomeone() {} // Dave is a violent Guy.
    }
    
    这段代码暗示Dave有一个人。这并不是那么简单,也不能解释它本身。而且,任何一个人能做的事情,Dave都能做,所以我们断言Dave是一个“人”是有道理的

    何时使用合成

    当我们只想公开类接口的一部分时,我们使用组合。按照前面的示例,假设
    Dave
    有一个
    吉他
    。吉他有一个更复杂的接口:

    public class Guitar
    {
         public Color color;
         // Guitar's color.
         public Tuning tuning;
         // Guitar's tuning.
    
         public void tuneGuitar()
         {}
    
         public void playChord()
         {}
    
         public void setColor()
         {}
    }
    
    现在,如果我们继承这个类,结果会是什么

    好吧,Dave类现在会有属性
    color
    tuning
    Dave
    有调谐吗?我想没有!这就是继承没有意义的地方。我们不想将整个
    吉他
    界面与
    Dave
    界面一起公开。我们只希望用户能够访问
    Dav的内容e
    需要访问,因此在这种情况下,我们将使用一些组合:

    public class Dave extends Person
    {
         private Guitar guitar;
         // Hide the guitar object. Then limit what the user can do with it.
    
         public void changeGuitarColor(Color newColor)
         {
             // So this code makes a lot more sense than if we had used inheritance.
             guitar.setColor(newColor);
         }
    
    
         public void speak() { System.out.println("Hello!"); }
         public void killSomeone() {} // Dave is a violent Guy.
    }
    
    结论 这并不是什么可以取代另一个。这是关于你在其中实现技术的情况。希望在本例结束时,你会看到继承适用于一个对象
    是一个
    对象的情况,当一个对象
    有一个
    对象时使用组合。

    问得好

    当你想在语义上区分“A是AA B”和“A有AA B”时,你可能更喜欢继承而不是组合。例如,类别汽车可能有一个发动机成员(组合),但可能是车辆(即从车辆继承)

    我认为区别在于必须维护两个类共享的方法的数量。继承时,只需重写基类中真正需要的(虚拟)函数;如果要使用纯合成,则需要重新实现成员类中的每个公共方法

    最终,“最佳”方法是最适合您当前应用程序的方法

    编辑:

    这里有一个简短的辩论:

    以及维基百科关于它的一篇文章(!):

    很抱歉发布这篇文章,但问题和答案似乎都集中在代码重用上。我们可以使用继承和组合来重用来自共同“祖先”的一些代码,这是真的。在这里,组合效果更好

    但是继承主要不是为了代码重用,而是为了动态绑定。实际上,可以将“is a”关系表述为:调用代码使用对象的接口,对特定实现一无所知

    伪代码:

    // Interface
    class A { 
      drawMe() {} ; 
    };
    // Implementations
    class Rect:A { 
      drawMe() {DrawRect();}; 
    };
    class Circle:A { 
      drawMe() {Drawcircle();}; 
    };
    
    main() {
      // We fill the array in one part of the program
      Array_of_A arr;
      arr.add(new Rect);
      arr.add(new Circle);
      // We can use the array in completely another part
      // without any idea of what the items really are,
      // only know the interface
      foreach(item from arr) {
         item->drawMe();
      }
    }
    
    在许多语言中(如C++),继承是说“这个对象实现了这个接口”的唯一实用方法。它还允许做一些事情,比如代码重用,这在组合中做得更好

    我对Go一无所知,但我正确地理解了您的话,它提供了另一种定义“此对象实现此接口”的方法:

    每个对象都有一个接口定义的方法,实现这个i
    // Interface
    class A { 
      drawMe() {} ; 
    };
    // Implementations
    class Rect:A { 
      drawMe() {DrawRect();}; 
    };
    class Circle:A { 
      drawMe() {Drawcircle();}; 
    };
    
    main() {
      // We fill the array in one part of the program
      Array_of_A arr;
      arr.add(new Rect);
      arr.add(new Circle);
      // We can use the array in completely another part
      // without any idea of what the items really are,
      // only know the interface
      foreach(item from arr) {
         item->drawMe();
      }
    }