2013-09-30 99 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

相關問題