这个问题是不是 像“继承与组合”这样的问题。
我完全理解继承与组合有何不同,我知道 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 类现在将具有属性
color
和 tuning
.是否 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/