Estou familiarizado com o fato de que, em Go, as interfaces definem funcionalidade, em vez de dados. Você coloca um conjunto de métodos em uma interface, mas não consegue especificar nenhum campo que seria necessário em qualquer coisa que implemente essa interface.
Por exemplo:
// 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
}
Agora podemos usar a interface e suas implementações:
// 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
*/
Agora, o que você não pode fazer é algo assim:
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)
}
No entanto, depois de brincar com interfaces e estruturas incorporadas, descobri uma maneira de fazer isso, de certa forma:
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
}
Por causa da estrutura incorporada, Bob tem tudo que Person tem. Ele também implementa a interface PersonProvider, para que possamos passar Bob para funções projetadas para usar essa interface.
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)
}
Aqui está um Go Playground que demonstra o código acima.
Usando esse método, posso fazer uma interface que define dados em vez de comportamento, e que pode ser implementada por qualquer estrutura apenas incorporando esses dados. Você pode definir funções que interagem explicitamente com esses dados incorporados e não estão cientes da natureza da estrutura externa. E tudo é verificado em tempo de compilação! (A única maneira de você bagunçar, pelo que eu posso ver, seria incorporar a interface PersonProvider
em Bob
, em vez de um concreto Person
. Ele iria compilar e falhar no tempo de execução.)
Agora, aqui está a minha pergunta: este é um truque legal ou eu deveria fazer diferente?