【发布时间】:2014-11-19 13:31:33
【问题描述】:
我很熟悉这样一个事实,在 Go 中,接口定义的是功能,而不是数据。您将一组方法放入接口中,但您无法指定实现该接口的任何对象所需的任何字段。
例如:
// Interface
type Giver interface {
Give() int64
}
// One implementation
type FiveGiver struct {}
func (fg *FiveGiver) Give() int64 {
return 5
}
// Another implementation
type VarGiver struct {
number int64
}
func (vg *VarGiver) Give() int64 {
return vg.number
}
现在我们可以使用接口及其实现了:
// A function that uses the interface
func GetSomething(aGiver Giver) {
fmt.Println("The Giver gives: ", aGiver.Give())
}
// Bring it all together
func main() {
fg := &FiveGiver{}
vg := &VarGiver{3}
GetSomething(fg)
GetSomething(vg)
}
/*
Resulting output:
5
3
*/
现在,你不能做的是这样的:
type Person interface {
Name string
Age int64
}
type Bob struct implements Person { // Not Go syntax!
...
}
func PrintName(aPerson Person) {
fmt.Println("Person's name is: ", aPerson.Name)
}
func main() {
b := &Bob{"Bob", 23}
PrintName(b)
}
然而,在玩弄了接口和嵌入式结构之后,我发现了一种方法可以做到这一点,而且很流行:
type PersonProvider interface {
GetPerson() *Person
}
type Person struct {
Name string
Age int64
}
func (p *Person) GetPerson() *Person {
return p
}
type Bob struct {
FavoriteNumber int64
Person
}
由于嵌入式结构,Bob 拥有 Person 拥有的一切。它还实现了 PersonProvider 接口,因此我们可以将 Bob 传递给旨在使用该接口的函数。
func DoBirthday(pp PersonProvider) {
pers := pp.GetPerson()
pers.Age += 1
}
func SayHi(pp PersonProvider) {
fmt.Printf("Hello, %v!\r", pp.GetPerson().Name)
}
func main() {
b := &Bob{
5,
Person{"Bob", 23},
}
DoBirthday(b)
SayHi(b)
fmt.Printf("You're %v years old now!", b.Age)
}
Here is a Go Playground 演示了上面的代码。
使用这种方法,我可以创建一个定义数据而不是行为的接口,并且可以通过嵌入该数据由任何结构实现。您可以定义与该嵌入数据显式交互并且不知道外部结构的性质的函数。并且在编译时检查所有内容! (我可以看到,唯一可能搞砸的方法是将接口 PersonProvider 嵌入到 Bob 中,而不是具体的 Person。它会在运行时编译并失败。)
现在,我的问题是:这是一个巧妙的技巧,还是我应该换一种方式?
【问题讨论】:
-
“我可以创建一个定义数据而不是行为的接口”。我认为你有一种返回数据的行为。
-
我要写一个答案;我认为如果你需要它并且知道后果就可以了,但是有后果我不会一直这样做。
-
@jmaloney 我认为你是对的,如果你想清楚地看待它。但总的来说,随着我展示的不同部分,语义变成“这个函数接受任何在其组成中具有 ___ 的结构”。至少,这是我的本意。
-
这不是“答案”材料。我通过谷歌搜索“interface as struct property golang”来回答你的问题。我通过将实现接口的结构设置为另一个结构的属性找到了类似的方法。这里是操场,play.golang.org/p/KLzREXk9xo 谢谢你给我一些想法。
-
回想起来,在使用 Go 5 年后,我很清楚以上不是惯用的 Go。这是对仿制药的一种压力。如果你想做这种事情,我建议你重新考虑你的系统架构。接受接口并返回结构,通过交流来分享,并为之欢欣鼓舞。