我希望将“经典OO”示例转换为Go,其中一组子类自己实现某些方法,但是它们通过超类共享一些方法的实现。我很清楚如何使用Go的界面,我甚至使用嵌入,但我不太清楚用什么(如果有的话)习惯来捕捉这种预期的行为。
这是一个具体的,可能是一个非常熟悉的例子。我会用Ruby。有两种动物,狗和奶牛。所有的动物都有一个名字,他们可以说话。无论动物类型如何,你设定和获得相同的方式都是一样的;他们制作的声音因子类而异。现在有一个speak
方法对于所有动物都是相同的,但它会委托子类的sound
方法。这是在Ruby中:
class Animal
def initialize(name); @name = name; end
def speak; puts "#{@name} says #{sound()}"; end
end
class Dog < Animal; def sound(); "woof"; end; end
class Cow < Animal; def sound(); "mooo"; end; end
如何最好地在Go中捕获?
到目前为止,我已经尝试了
type Animal struct {
name string
}
type Cow struct {
Animal
}
type Dog struct {
Animal
}
我能够像这样构建“动物”:
func (d Dog) sound() string {return "woof"}
func (c Cow) sound() string {return "mooo"}
func main() {
d := Dog{Animal{"Sparky"}}
c := Cow{Animal{"Bessie"}}
fmt.Println(d.name)
fmt.Println(c.sound())
}
但我觉得我这一切都错了。我知道我可以将sound()
放在一个界面中,但那时特定的动物是发声器,而不是真正的动物。如果Animal
成为界面,我无法共享姓名和发言代码。我意识到Go的设计者只使用了接口,并选择不直接支持这个经典的OO用例,就像我们在Ruby,Python,Java等中看到它一样,但我怀疑应该有一些用于模拟此功能的习惯用法或最佳做法。这样做的首选方式是什么?
答案 0 :(得分:4)
但我怀疑应该有一些习惯用法或最佳做法来模拟这个。
没有。
如果出现类似的东西(并且在实际代码中并不常见,但主要是在Java / Ruby /任何代码的翻译中):interface Named { Name() string }
和interface Sounder { Sound() }
合并到{{ 1}}并传递这些动物。
再次:“首选方式”是在没有继承的情况下重新构建解决方案。
答案 1 :(得分:3)
我认为混淆可能来自使用composite literals
构建实例。
这些非常适合在单行中创建复杂类型,并且如前面的链接所示,可以管理减少锅炉板代码。
但是,有时候,通过更明确地执行操作,代码可能更简单,更易读。我发现利用Embedding时有时会出现这种情况。
引用上一个链接:
嵌入式类型的方法是免费的
你不委托子类的sound
方法,但“子类”sound
的设置和获取透明地使用{{1 } sound
所以我这样做的首选方式是:
Animal
产地:
MOOO
纬
编辑:有关此主题的a quote from Rob Pike:
Go采用了一种不寻常的方法来进行面向对象的编程,允许任何类型的方法,而不仅仅是类,但没有任何形式的基于类型的继承,如子类化。这意味着没有类型层次结构。这是一个有意的设计选择。虽然类型层次结构已被用于构建非常成功的软件,但我们认为该模型已被过度使用,值得退一步。
答案 2 :(得分:2)
您无法将非接口方法附加到接口。如果动物说话,他们需要一个名字和一个声音。您还可以嵌入私有类型,嵌入的是实现细节。鉴于这些见解,我认为这就是你所追求的目标。
package farm
type Animal interface {
Name() string
Sound() string
}
func Speak(a Animal) string {
return a.Name() + " says " + a.Sound()
}
type animal struct {
name string
}
func (a *animal) Name() string {
return a.name
}
type Cow struct {
animal
}
func NewCow(name string) *Cow {
return &Cow{animal{name}}
}
func (c *Cow) Sound() string {
return "mooo"
}
type Dog struct {
animal
}
func NewDog(name string) *Dog {
return &Dog{animal{name}}
}
func (c *Dog) Sound() string {
return "woof"
}
这样的主要:
package main
import "fmt"
import "farm"
func main() {
c := farm.NewCow("Betsy")
d := farm.NewDog("Sparky")
//"In classic OOO you'd write c.Speak()"
fmt.Println(farm.Speak(c))
fmt.Println(farm.Speak(d))
}
播放链接w / main:http://play.golang.org/p/YXX6opX8Cy
答案 3 :(得分:0)
这个怎么样?
package main
import (
"fmt"
)
type Sounder interface {
Sound() string
}
type Animal struct {
Name string
Sounder Sounder
}
func (a *Animal) Speak() {
fmt.Printf("%s says %s.\n", a.Name, a.Sounder.Sound())
}
type StringSounder string
func (f StringSounder) Sound() string {
return string(f)
}
func main() {
d := &Animal{"Sparky", StringSounder("woof")}
c := &Animal{"Bessie", StringSounder("mooo")}
d.Speak()
c.Speak()
}