Ho familiarità con il fatto che, in Go, le interfacce definiscono la funzionalità, piuttosto che i dati. Metti una serie di metodi in un'interfaccia, ma non sei in grado di specificare alcun campo che sarebbe richiesto su qualsiasi cosa che implementi quell'interfaccia.
Per esempio:
// 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
}
Ora possiamo usare l'interfaccia e le sue implementazioni:
// 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
*/
Quello che non puoi fare è qualcosa del genere:
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)
}
Tuttavia, dopo aver giocato con interfacce e strutture incorporate, ho scoperto un modo per farlo, in un certo senso:
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
}
A causa della struttura incorporata, Bob ha tutto ciò che ha Person. Implementa anche l'interfaccia PersonProvider, quindi possiamo passare Bob in funzioni progettate per usare quell'interfaccia.
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)
}
Ecco un Go Playground che dimostra il codice sopra.
Usando questo metodo, posso creare un'interfaccia che definisce i dati piuttosto che il comportamento e che può essere implementata da qualsiasi struttura semplicemente incorporando quei dati. È possibile definire funzioni che interagiscono esplicitamente con i dati incorporati e non sono consapevoli della natura della struttura esterna. E tutto viene controllato in fase di compilazione! (L'unico modo per rovinare, che posso vedere, sarebbe incorporare l'interfaccia PersonProvider
in Bob
, piuttosto che una concreta Person
. Sarebbe la compilazione e non riescono a runtime.)
Ora, ecco la mia domanda: è un bel trucco o dovrei farlo in modo diverso?