在Go中建模“超类方法实现”的最佳方法是什么?

时间:2013-09-30 06:21:09

标签: ruby inheritance interface go

我希望将“经典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等中看到它一样,但我怀疑应该有一些用于模拟此功能的习惯用法或最佳做法。这样做的首选方式是什么?

4 个答案:

答案 0 :(得分:4)

  

但我怀疑应该有一些习惯用法或最佳做法来模拟这个。

没有。

如果出现类似的东西(并且在实际代码中并不常见,但主要是在Java / Ruby /任何代码的翻译中):interface Named { Name() string }interface Sounder { Sound() }合并到{{ 1}}并传递这些动物。

再次:“首选方式”是在没有继承的情况下重新构建解决方案。

答案 1 :(得分:3)

我认为混淆可能来自使用composite literals构建实例。

这些非常适合在单行中创建复杂类型,并且如前面的链接所示,可以管理减少锅炉板代码。

但是,有时候,通过更明确地执行操作,代码可能更简单,更易读。我发现利用Embedding时有时会出现这种情况。

引用上一个链接:

  
    

嵌入式类型的方法是免费的

  

委托子类的sound方法,但“子类”sound的设置和获取透明地使用{{1 } sound

的字段

所以我这样做的首选方式是:

Animal

产地:

  
    

MOOO
    纬

  

Playgound link

编辑:有关此主题的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()
}