我知道在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)
}
这里有一个Go Playground,可以演示上面的代码。
使用这种方法,我可以创建一个定义数据而非行为的接口,并且任何构造体只要嵌入该数据就能够实现该接口。你可以定义与这个嵌入式数据进行显式交互的函数,这些函数不知道外部结构体的性质。并且一切都在编译时进行检查!(我唯一能想到的搞砸的方法是将接口PersonProvider
嵌入到Bob
中,而不是具体的Person
。它会编译但在运行时失败。)
现在,我的问题是:这是一个好方法,还是我应该采用其他方式?