继承可以完全被组成所取代吗? [英] Can inheritance be replaced completely by composition?

查看:97
本文介绍了继承可以完全被组成所取代吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题是问题,例如继承与构成".

我完全了解继承与组合有何不同,我知道 Liskov替代原理,即钻石问题,它们的优点和缺点以及这两个概念似乎都很简单.但是到处都有很多关于继承和组合的问题,我想,也许我误解了这个简单的想法.

让我们专注于开始. Go是Google的一种语言,每个人都感到兴奋,它没有继承,没有类,但是它具有组成部分,这很酷. 对我而言,Go中的合成为您提供了完全与其他语言(C ++,Java等)继承相同的功能-组件方法会自动公开,并可以用作后续结构的方法,例如此处:

 package main

import (
    "fmt"
)

type Car struct{
    name string
}

func (c *Car) move() bool { 
    return true
} 

type MyCar struct{
    Car   
}

func main() {
    var c MyCar
    fmt.Print(c.move())
}
 

总而言之,组合比继承更好,因为:

  1. 更灵活(允许您在运行时更改组件,因此可以影响类"的工作方式.
  2. 没有钻石问题(但是钻石问题是可以解决的,所以这不是很强的优势)

如果您考虑使用Go及其接口(每个对象(具有通过接口定义的方法,都实现此接口隐式实现)),您是否有最终解决方案? 我们可以说用一些语法糖组成的组合可以代替继承吗?

这种设计符合Liskov替代原则.我是否会错过某些东西或继承(从任何语言中得知)都没有从Go已知的组成(和接口)中受益?

===== edit1 =====

为澄清起见,在Go中可以使用标准"组合机制,如下所示(此示例的行为类似于上一个):

package main

import (
    "fmt"
)

type Car struct{
    name string
}

func (c *Car) move() bool { 
    return true
} 

type MyCar struct{
    car Car
}

func (c *MyCar) move() bool { 
    return c.car.move()
} 

func main() {
    var c MyCar
    fmt.Print(c.move())
}

但是,如果像上一个示例那样使用它,则所有方法都可以在"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)有一个人.它不是那么简单,也无法自我解释.另外,任何人可以做的事情,戴夫都可以做,因此我们断言戴夫是人"是有道理的.

何时使用合成

当我们只想公开类的接口的一部分时,我们使用Composition.在前面的示例之后,假设Dave具有一个Guitar.吉他具有更复杂的界面:

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类现在将具有属性colortuning. Dave是否有调音功能?我想不是!这是继承没有意义的地方.我们不想同时公开整个Guitar接口和Dave接口.我们只希望用户能够访问Dave需要访问的内容,因此在这种情况下,我们将使用一些组合:

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.
}

结论

这真的不是什么可以替代另一个的情况.这是关于您正在实现这些技术的情况.希望,在示例结束时,您将看到继承是针对一种对象IS A对象,而当一种对象HAS A对象使用组合的情况. /p>

This question is NOT question like "inheritence vs composition".

I understand completely how inheritance differs from composition, I know the Liskov substitution principle, the diamond problem, advantages and disadvantages both of them and both concepts seem to be simple. But there is so many questions everywhere about inheritance and composition, that i thought, maybe I misunderstand something in this simple idea.

Lets focus on Go. Go is a language from Google and everybody is excited it has no inheritance, it has no classes, but it has composition and this is cool. For me, the composition in Go gives you exactly the same functionality as inheritance in other languages (C++, Java, ...) - component methods are automatically exposed and available as methods of later structs, like here:

package main

import (
    "fmt"
)

type Car struct{
    name string
}

func (c *Car) move() bool { 
    return true
} 

type MyCar struct{
    Car   
}

func main() {
    var c MyCar
    fmt.Print(c.move())
}

So to sum everything up, composition is better than inheritance because:

  1. is more flexible (allows you to change the component in runtime, so you can affect the way "classes" work.
  2. is free from diamond problem (but diamond problem is solvable, so this is not strong advantage)

And if you consider Go and its interfaces (every object, that has methods defined by an interface, implements this interface implicite) do you have the ultimate solution? Can we say that composition with some syntactic sugar can replace inheritance?

Such design agrees with Liskov substitution principle. Do I miss something or inheritance (known from any language) has no advantages over composition (and interfaces) known from Go?

===== edit1 =====

For clarification, it is possible in Go to use "standard" composition mechanism, like this (this example behaves like the previous one):

package main

import (
    "fmt"
)

type Car struct{
    name string
}

func (c *Car) move() bool { 
    return true
} 

type MyCar struct{
    car Car
}

func (c *MyCar) move() bool { 
    return c.car.move()
} 

func main() {
    var c MyCar
    fmt.Print(c.move())
}

But if you use it like in the previous example, all the methods are available implicite "in MyCar class".

解决方案

The Short Answer

It's really not as black and white as that. In short, yes. Any situation that can be solved with inheritance can be solved, near enough, by composition. So in short, the answer to your question is yes; inheritance can be replaced by composition.

Why it's not that simple

When to use Inheritance
It's not a matter of whether you CAN swap them out. It depends on the context that you're programming in, and it becomes more of a question of whether you SHOULD swap them out. Take this simple example in Java:

public class Person
{
    // Assume every person can speak.
    public void speak()
    {
    }
}

Now, let's say we have another class, Dave. Dave IS a person.

public class Dave extends Person
{
     public void speak() { System.out.println("Hello!"); }
     public void killSomeone() {} // Dave is a violent Guy.
}

Now would it make more sense for the class Dave to look like this?

public class Dave
{
     private Person p;
     // Composition variant.

     public void speak() { p.speak(); }
     public void killSomeone() {} // Dave is a violent Guy.
}

This code implies Dave has a person. It's not as simple and doesn't explain itself as well. Also, anything a Person can do, Dave can do, so it makes sense that we assert Dave is a "Person".

When to use Composition

We use Composition when we only want to expose part of the class' interface. Following our previous example, let's say Dave has a Guitar. The guitar has a more complex interface:

public class Guitar
{
     public Color color;
     // Guitar's color.
     public Tuning tuning;
     // Guitar's tuning.

     public void tuneGuitar()
     {}

     public void playChord()
     {}

     public void setColor()
     {}
}

Now, if we were to inherit this class, what would the outcome be?

Well, class Dave would now have attributes color and tuning. Does Dave have a tuning? I think not! This is where inheritance makes no sense. We don't want to expose the entire Guitar interface along with the Dave interface. We only want the user to be able to access what Dave needs to access, so in this case we would use some composition:

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.
}

Conclusion

It's really not a case of what can replace the other. It's about the situation that you are implementing the techniques in. Hopefully, by the end of the example you'll see that inheritance is for situations where one object IS A object, and composition is used when one object HAS A object.

这篇关于继承可以完全被组成所取代吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆