在 Go 中编写接口而不重复自己
Coding to an interface in Go without repeating yourself
假设我有两只宠物,一只叫露西的猫和一只叫菲多的狗。我教了他们同样的把戏,"speak"。
以后想养更多的宠物,教给它们不同的把戏,所以心存期待,写了一个接口:
package main
import "fmt"
type Pet interface {
speak() string
}
type Dog struct {
speech string
}
type Cat struct {
speech string
}
func (c Cat) speak() string {
return c.speech
}
func (d Dog) speak() string {
return d.speech
}
func getSpeech(p Pet) string {
return p.speak()
}
func main() {
Fido := Dog{"woof"}
Lucy := Cat{"meow"}
fmt.Println("Fido says:", getSpeech(Fido)) // Fido says: woof
fmt.Println("Lucy says:", getSpeech(Lucy)) // Lucy says: meow
}
现在,虽然这很好用,但似乎不必要地冗长。我清楚地重复自己。另外,假设所有的狗都说 "woof" 而所有的猫都说 "meow",在结构中初始化字符串是惯用的吗?
您将如何在不失去接口优势的情况下将此代码重构为更 DRY?
首先:我在您的代码中看不到任何重复:您有猫和狗,每只猫可能会说些什么,每只狗可能会说些什么。如果情况并非如此并且您的假设是正确的
如果所有的狗都汪汪,所有的猫都喵喵叫怎么办:
const dogTalk = "woof"
func (d Dog) speak() string { return dogTalk; }
// or even
func (d Cat) speak() string { return "meow"; }
(并且:不要在 Go 中编写 Java)
在某些情况下,您可以嵌入一个基类型来委托公共字段和方法。这不是继承,它只是一种通过组合自动委托的形式。不要像在 java 风格的 OOP 语言中那样尝试使用它来创建类型层次结构。
这里可以将speak
方法委托给Speaker
类型。在实践中,这不太有用,因为 Speaker
及其方法与它们嵌入的结构没有关系,即 Speaker 不知道它在说哪种类型,也不知道哪个实例。
https://play.golang.org/p/Bof92jZsNh
type Speaker struct {
Saying string
}
func (s Speaker) speak() string {
return s.Saying
}
type Pet interface {
speak() string
}
type Dog struct {
Speaker
}
type Cat struct {
Speaker
}
func getSpeech(p Pet) string {
return p.speak()
}
func main() {
Fido := Dog{Speaker{Saying: "woof"}}
Lucy := Cat{Speaker{Saying: "meow"}}
fmt.Println("Fido says:", getSpeech(Fido)) // Fido says: woof
fmt.Println("Lucy says:", getSpeech(Lucy)) // Lucy says: meow
}
你可以这样做。
package main
import "fmt"
type Pet interface{ speak() string }
type Speaker struct{ Saying string }
func (s Speaker) speak() string { return s.Saying }
func getSpeech(p Pet) string { return p.speak() }
func main() {
Fido := Speaker{Saying: "woof"}
Lucy := Speaker{Saying: "meow"}
fmt.Println("Fido says:", getSpeech(Fido)) // Fido says: woof
fmt.Println("Lucy says:", getSpeech(Lucy)) // Lucy says: meow
}
我尽量不让代码太枯燥以至于容易擦伤。我可以接受重复两次的代码,并且只考虑第三次重复的代码。
假设我有两只宠物,一只叫露西的猫和一只叫菲多的狗。我教了他们同样的把戏,"speak"。
以后想养更多的宠物,教给它们不同的把戏,所以心存期待,写了一个接口:
package main
import "fmt"
type Pet interface {
speak() string
}
type Dog struct {
speech string
}
type Cat struct {
speech string
}
func (c Cat) speak() string {
return c.speech
}
func (d Dog) speak() string {
return d.speech
}
func getSpeech(p Pet) string {
return p.speak()
}
func main() {
Fido := Dog{"woof"}
Lucy := Cat{"meow"}
fmt.Println("Fido says:", getSpeech(Fido)) // Fido says: woof
fmt.Println("Lucy says:", getSpeech(Lucy)) // Lucy says: meow
}
现在,虽然这很好用,但似乎不必要地冗长。我清楚地重复自己。另外,假设所有的狗都说 "woof" 而所有的猫都说 "meow",在结构中初始化字符串是惯用的吗?
您将如何在不失去接口优势的情况下将此代码重构为更 DRY?
首先:我在您的代码中看不到任何重复:您有猫和狗,每只猫可能会说些什么,每只狗可能会说些什么。如果情况并非如此并且您的假设是正确的
如果所有的狗都汪汪,所有的猫都喵喵叫怎么办:
const dogTalk = "woof"
func (d Dog) speak() string { return dogTalk; }
// or even
func (d Cat) speak() string { return "meow"; }
(并且:不要在 Go 中编写 Java)
在某些情况下,您可以嵌入一个基类型来委托公共字段和方法。这不是继承,它只是一种通过组合自动委托的形式。不要像在 java 风格的 OOP 语言中那样尝试使用它来创建类型层次结构。
这里可以将speak
方法委托给Speaker
类型。在实践中,这不太有用,因为 Speaker
及其方法与它们嵌入的结构没有关系,即 Speaker 不知道它在说哪种类型,也不知道哪个实例。
https://play.golang.org/p/Bof92jZsNh
type Speaker struct {
Saying string
}
func (s Speaker) speak() string {
return s.Saying
}
type Pet interface {
speak() string
}
type Dog struct {
Speaker
}
type Cat struct {
Speaker
}
func getSpeech(p Pet) string {
return p.speak()
}
func main() {
Fido := Dog{Speaker{Saying: "woof"}}
Lucy := Cat{Speaker{Saying: "meow"}}
fmt.Println("Fido says:", getSpeech(Fido)) // Fido says: woof
fmt.Println("Lucy says:", getSpeech(Lucy)) // Lucy says: meow
}
你可以这样做。
package main
import "fmt"
type Pet interface{ speak() string }
type Speaker struct{ Saying string }
func (s Speaker) speak() string { return s.Saying }
func getSpeech(p Pet) string { return p.speak() }
func main() {
Fido := Speaker{Saying: "woof"}
Lucy := Speaker{Saying: "meow"}
fmt.Println("Fido says:", getSpeech(Fido)) // Fido says: woof
fmt.Println("Lucy says:", getSpeech(Lucy)) // Lucy says: meow
}
我尽量不让代码太枯燥以至于容易擦伤。我可以接受重复两次的代码,并且只考虑第三次重复的代码。