Ich bin mit der Tatsache vertraut, dass in Go Schnittstellen eher Funktionalität als Daten definieren. Sie fügen eine Reihe von Methoden in eine Schnittstelle ein, können jedoch keine Felder angeben, die für irgendetwas erforderlich wären, das diese Schnittstelle implementiert.
Beispielsweise:
// 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
}
Jetzt können wir die Schnittstelle und ihre Implementierungen verwenden:
// 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
*/
Was Sie jetzt nicht tun können, ist ungefähr so:
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)
}
Nachdem ich jedoch mit Schnittstellen und eingebetteten Strukturen herumgespielt habe, habe ich einen Weg gefunden, dies auf folgende Weise zu tun:
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
}
Aufgrund der eingebetteten Struktur hat Bob alles, was Person hat. Es implementiert auch die PersonProvider-Schnittstelle, sodass wir Bob an Funktionen übergeben können, die für die Verwendung dieser Schnittstelle ausgelegt sind.
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)
}
Hier ist ein Go-Spielplatz , der den obigen Code demonstriert.
Mit dieser Methode kann ich eine Schnittstelle erstellen, die eher Daten als Verhalten definiert und die von jeder Struktur implementiert werden kann, indem diese Daten einfach eingebettet werden. Sie können Funktionen definieren, die explizit mit diesen eingebetteten Daten interagieren und sich der Natur der äußeren Struktur nicht bewusst sind. Und alles wird beim Kompilieren überprüft! (Die einzige Möglichkeit, vermasseln könnte, dass ich sehen kann, würde die Schnittstelle werden die Einbettung PersonProvider
in Bob
, sondern als ein Beton Person
. Es wäre kompilieren und zur Laufzeit fehl.)
Hier ist meine Frage: Ist das ein ordentlicher Trick oder sollte ich es anders machen?