Estoy familiarizado con el hecho de que, en Go, las interfaces definen la funcionalidad, en lugar de los datos. Pones un conjunto de métodos en una interfaz, pero no puedes especificar ningún campo que sea necesario en cualquier cosa que implemente esa interfaz.
Por ejemplo:
// 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
}
Ahora podemos usar la interfaz y sus implementaciones:
// 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
*/
Ahora, lo que no puedes hacer es algo como esto:
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)
}
Sin embargo, después de jugar con interfaces y estructuras incrustadas, descubrí una forma de hacer esto, de alguna manera:
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
}
Debido a la estructura incrustada, Bob tiene todo lo que tiene Person. También implementa la interfaz PersonProvider, por lo que podemos pasar a Bob a funciones que están diseñadas para usar esa interfaz.
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)
}
Aquí hay un Go Playground que demuestra el código anterior.
Con este método, puedo crear una interfaz que defina datos en lugar de comportamiento, y que puede ser implementada por cualquier estructura simplemente incorporando esos datos. Puede definir funciones que interactúan explícitamente con esos datos incrustados y desconocen la naturaleza de la estructura externa. ¡Y todo se comprueba en tiempo de compilación! (Puedo ver que la única forma en que podría estropearlo sería incrustar la interfaz PersonProvider
en Bob
, en lugar de una concreta Person
. Se compilaría y fallaría en tiempo de ejecución).
Ahora, aquí está mi pregunta: ¿es este un buen truco o debería hacerlo de manera diferente?