Je connais le fait que, dans Go, les interfaces définissent la fonctionnalité plutôt que les données. Vous placez un ensemble de méthodes dans une interface, mais vous ne pouvez pas spécifier de champs qui seraient requis sur tout ce qui implémente cette interface.
Par exemple:
// 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
}
Nous pouvons maintenant utiliser l'interface et ses implémentations:
// 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
*/
Maintenant, ce que vous ne pouvez pas faire est quelque chose comme ceci:
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)
}
Cependant, après avoir joué avec les interfaces et les structures embarquées, j'ai découvert un moyen de le faire, d'une certaine manière:
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
}
En raison de la structure intégrée, Bob a tout ce que Person a. Il implémente également l'interface PersonProvider, afin que nous puissions transmettre à Bob des fonctions conçues pour utiliser cette 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)
}
Voici un Go Playground qui illustre le code ci-dessus.
En utilisant cette méthode, je peux créer une interface qui définit les données plutôt que le comportement, et qui peut être implémentée par n'importe quelle structure simplement en incorporant ces données. Vous pouvez définir des fonctions qui interagissent explicitement avec ces données intégrées et qui ne connaissent pas la nature de la structure externe. Et tout est vérifié au moment de la compilation! (La seule façon dont vous pourriez gâcher, que je peux voir, serait d'incorporer l'interface PersonProvider
dans Bob
, plutôt que dans un béton Person
. Cela compilerait et échouerait à l'exécution.)
Maintenant, voici ma question: est-ce un bon truc, ou devrais-je le faire différemment?