Lektionen des Moduls (3/5)
Komposition (Embedding)
Go hat keine Vererbung. Die idiomatische Antwort auf die Wiederverwendung lautet Zusammensetzung und insbesondere Einbettung: einschließlich eines Typs innerhalb eines anderen, ohne ihm einen Feldnamen zu geben. Die Bereiche und Methoden von Der eingebettete Typ wird heraufgestuft und wird so direkt zugänglich sie gehörten zum äußeren Typus.
Struktureinbettung
type Animal struct {
Name string
}
func (a Animal) Greet() string {
return "ciao da " + a.Name
}
type Dog struct {
Animal // embedding — NO field name
Breed string
}
c := Dog{
Animal: Animal{Name: "Rex"},
Breed: "Pastore",
}
fmt.Println(c.Name) // "Rex" — promoted from Animal
fmt.Println(c.Greet()) // "ciao da Rex" — promoted method
fmt.Println(c.Breed) // own fieldDas eingebettete Feld ist weiterhin über den Typnamen zugänglich:
c.Animal.Name funktioniert und ist identisch mit c.Name. Es wird benötigt, wenn
es kommt zu Kollisionen.
Override (teilweise): Methode „Shadowing“
Wenn der äußere Typ eine Methode mit demselben Namen wie die heraufgestufte definiert Erstens „gewinnt“ seine Methode. Von innen ist das Eingebettete jederzeit erreichbar eine über den Namen des Typs:
func (c Dog) Greet() string {
return "BAU! " + c.Animal.Greet()
}Es handelt sich nicht um eine polymorphe Vererbung, sondern lediglich um eine lexikalische Namensauflösung. Für Polymorphismus verwenden Sie Schnittstellen (Modul 6).
Mehrfacheinbettung
Sie können mehrere Typen einbetten:
type Logger struct{ /*...*/ }
func (l Logger) Log(msg string) { /*...*/ }
type Timer struct{ /*...*/ }
func (t Timer) Now() time.Time { /*...*/ }
type Service struct {
Logger
Timer
Name string
}
s := Service{Name: "api"}
s.Log("start") // promoted from Logger
s.Now() // promoted from TimerWenn zwei eingebettete Typen eine Methode mit DEM GLEICHEN Namen haben, direkter Zugriff
wird mehrdeutig und der Compiler benötigt die explizite Form
(s.Logger.Foo() oder s.Timer.Foo()).
Schnittstelleneinbettung
Schnittstellen können auch „eingebettet“ werden (siehe Modul 6):
type ReadWriter interface {
io.Reader
io.Writer
}Es entspricht der Vereinigung der Methoden von Reader und Writer.
Wann sollte eine explizite Komposition verwendet werden (keine Einbettung)?
Wenn Sie ein benanntes Feld und keine automatische Heraufstufung wünschen, verwenden Sie „plain“. Zusammensetzung:
type Dog struct {
Anim Animal // NORMAL field, no promotion
Breed string
}
c.Anim.Name // you need the field nameDas Einbetten ist ein praktisches Delegierungstool, keine Designauswahl Grundsätzlich besser als eine explizite Komposition: Verwenden Sie sie, wenn Sie es wirklich wollen um die API des inneren Typs zu fördern.
Probieren Sie es aus
Definieren Sie „Hund“, der „Tier“ einbettet (Einbettung, kein Feldname) und über ein Rassenzeichenfolgenfeld verfügt.
Hinweis anzeigen
Einbettung: Schreiben Sie den TYP, ohne ihm einen Feldnamen zu geben.
Lösung nach 3 Versuchen verfügbar
Fügen Sie Animal eine Greet()-String-Methode hinzu und rufen Sie sie direkt für eine Dog-Variable auf (Methodenförderung).
Hinweis anzeigen
Die auf „Animal“ definierte Methode wird dank der Einbettung auf „Dog“ hochgestuft.
Lösung nach 3 Versuchen verfügbar
Unterstützt Go die klassische Vererbung?
type B struct { /* extends A? */ }Zusammenfassung
- Keine Vererbung: Sie komponieren mit Einbettung.
- Einbetten = Schreiben des TYPE ohne Feldnamen in die Struktur. – Felder und Methoden des eingebetteten Typs werden heraufgestuft.
- Expliziter Zugriff ist immer verfügbar:
outer.Inner.Field. - „Lexikalische“ Überschreibung: Die Methode des äußeren Typs überschattet die hochgestufte.
- Einbettung ≠ is-a: Für Polymorphismus Schnittstellen verwenden.