gpt4 book ai didi

oop - 继承可以完全被组合取代吗?

转载 作者:行者123 更新时间:2023-12-04 07:24:35 25 4
gpt4 key购买 nike

这个问题是不是 像“继承与组合”这样的问题。

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

让我们关注Go . 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())
}

综上所述,组合优于继承,因为:
  • 更灵活(允许您在运行时更改组件,因此您可以影响“类”的工作方式。
  • 没有菱形继承(钻石问题)(但菱形继承(钻石问题)是可以解决的,所以这不是很强的优势)

  • 如果你考虑 Go 和它的接口(interface)(每个对象,具有由接口(interface)定义的方法,实现这个接口(interface)隐含)你有最终的解决方案吗?
    我们可以说带有一些语法糖的组合可以代替继承吗?


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

    ===== 编辑1 =====

    为澄清起见,可以在 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.
    }

    这段代码暗示戴夫有一个人。它没有那么简单,也没有解释自己。此外,任何人能做的事,戴夫都能做,所以我们断言戴夫是一个“人”是有道理的。

    何时使用合成

    当我们只想公开部分类的接口(interface)时,我们使用组合。按照我们之前的示例,假设 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接口(interface)连同 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目的。

    关于oop - 继承可以完全被组合取代吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15749316/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com