Woj*_*ilo 8 language-agnostic oop inheritance design-patterns composition
这个问题不是 "遗产与构成"之类的问题.
我完全理解继承与组合有什么不同,我知道Liskov替换原理,钻石问题,它们的优点和缺点以及这两个概念似乎都很简单.但到处都有关于继承和构成的问题,我想,也许我误解了这个简单的想法.
让我们关注Go.Go是一种来自谷歌的语言,每个人都很兴奋它没有继承,它没有类,但它有组成,这很酷.对我来说,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())
}
Run Code Online (Sandbox Code Playgroud)
所以总结一下,组合比继承更好,因为:
如果您考虑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())
}
Run Code Online (Sandbox Code Playgroud)
但是如果你像前面的例子一样使用它,所有的方法都可以在MyCar类中使用.
这确实不是那么黑白分明。简而言之,是的。任何可以通过继承解决的情况都可以通过组合来解决(足够接近)。简而言之,你的问题的答案是肯定的;继承可以用组合来代替。
何时使用继承
这不是是否可以交换它们的问题。这取决于您正在编程的上下文,并且更多的是您是否应该交换它们的问题。以 Java 为例:
public class Person
{
// Assume every person can speak.
public void speak()
{
}
}
Run Code Online (Sandbox Code Playgroud)
现在,假设我们还有另一堂课,戴夫。戴夫是一个人。
public class Dave extends Person
{
public void speak() { System.out.println("Hello!"); }
public void killSomeone() {} // Dave is a violent Guy.
}
Run Code Online (Sandbox Code Playgroud)
现在,类Dave看起来像这样更有意义吗?
public class Dave
{
private Person p;
// Composition variant.
public void speak() { p.speak(); }
public void killSomeone() {} // Dave is a violent Guy.
}
Run Code Online (Sandbox Code Playgroud)
此代码暗示 Dave 有一个人。它并不那么简单,也无法解释自己。此外,任何一个人能做的事,戴夫也能做,所以我们断言戴夫是一个“人”是有道理的。
何时使用组合
当我们只想公开类接口的一部分时,我们可以使用组合。按照我们前面的示例,假设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()
{}
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我们继承这个类,结果会怎样呢?
那么,Dave 类现在将具有属性color和tuning。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.
}
Run Code Online (Sandbox Code Playgroud)
这确实不是一个可以替代另一个的情况。这是关于您正在实现这些技术的情况。希望在示例结束时您会看到继承适用于一个对象对象的情况IS A,而组合适用于一个对象HAS A对象的情况。