2013-09-30 64 views
2

我期待将一个“经典的OO”例子翻译成Go,其中一组子类自己实现一些方法,但它们通过它们的超类共享一些方法的实现。我非常了解如何使用Go的接口,甚至使用了嵌入,但我不太确定使用什么成语来捕获这种预期的行为。在Go中建立“超类方法实现”的最佳方式是什么?

下面是一个具体的,也许是一个非常熟悉的例子。我将使用Ruby。有两种动物,狗和牛。所有动物都有名字,他们可以说话。不管动物的类型如何,您设置和获得相同的方式都是一样的;他们发出的声音因子类而异。现在有一个speak方法,它对所有的动物都是相同的,但它委托给子类的sound方法。这里是红宝石:

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

但我怀疑应该有一些成语或模拟此最佳做法。

没有没有。

如果这样的事情不上来(这并不经常在实际的代码,但主要是在对Java /红宝石/不管代码翻译):interface Named { Name() string }interface Sounder { Sound() }相结合,interface Animal {Named, Sounder}和通过周围的动物。

再次:在“首选方式”是重塑不继承该溶液中。

3

我觉得困惑可以使用composite literals构建实例到来。

这些非常适合在单行中创建复杂类型,并像上一个链接所暗示的那样管理,以减少锅炉代码。

然而,有时候,通过更明确地做事,代码可能更简单,更易读。我发现在利用Embedding时有时会发生这种情况。

引述以前的链接:

嵌入式类型的方法一起前来免费

委托给子类的sound方法,但是“子类”sound的设置和获取透明地使用sound字段Animal

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

package main 

import "fmt" 

type Animal struct { 
    name string 
    sound string 
} 

type Cow struct { 
    Animal 
} 

type Dog struct { 
    Animal 
} 

func (a *Animal) Speak() string { 
    return fmt.Sprintf("%s", a.sound) 
} 

func main() { 
    c := new(Cow) 
    d := new(Dog) 
    c.name, c.sound = "Bessie", "mooo" 
    d.name, d.sound = "Sparky", "woof" 
    fmt.Println(c.Speak()) 
    fmt.Println(d.Speak()) 
} 

产地:

MOOO

Playgound link

编辑:有a quote from Rob Pike关于这个问题:

去接受一个不寻常的做法,以面向对象编程,允许在任何类型的方法,而不仅仅是类,但没有任何形式的基于类型的继承像子类。这意味着没有类型层次结构。这是一个有意的设计选择。尽管类型层次结构已被用于构建成功的软件,但我们认为该模型已被过度使用,值得向后退一步。

+0

很好的回答和+1给了相关的罗勃·派克报价。我知道我们并没有在Go中提到我正在问的确切类型的事情(实现继承),但似乎仍然应该有一种捕获像“All cows say mooo”这样的业务规则的方法。您的解决方案非常接近,可能是首选的Go方法,但是如果有任何方法可以将动物的类型与其解决方案中具有的优势相结合。 –

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() 
} 
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)) 
} 

播放链接瓦特/主:http://play.golang.org/p/YXX6opX8Cy

相关问题