Mehrere Structs einbetten
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 37 von 107.
In Go kannst du mehrere Structs in ein einzelnes Struct einbetten und so Verhaltensweisen aus verschiedenen Quellen kombinieren. Das ähnelt der Mehrfachvererbung in anderen Sprachen, folgt aber Gos Ansatz der Komposition.
type Logger struct{}
func (l Logger) Log(msg string) string {
return "LOG: " + msg
}
type Notifier struct{}
func (n Notifier) Notify(msg string) string {
return "NOTIFY: " + msg
}
type Service struct {
Name string
Logger
Notifier
}
Die Service-Struktur hat jetzt Zugriff auf Methoden beider eingebetteter Typen:
func main() {
s := Service{Name: "OrderService"}
fmt.Println(s.Log("started")) // LOG: gestartet
fmt.Println(s.Notify("new order")) // NOTIFY: neue Bestellung
}
Beim Einbetten mehrerer Strukturen kann ein Namenskonflikt auftreten, wenn zwei eingebettete Typen Methoden mit demselben Namen haben. Go löst diese Mehrdeutigkeit nicht automatisch auf:
type A struct{}
func (A) Greet() string { return "Hello from A" }
type B struct{}
func (B) Greet() string { return "Hello from B" }
type Combined struct {
A
B
}
func main() {
c := Combined{}
// c.Greet() - Kompilierungsfehler: mehrdeutiger Selektor
fmt.Println(c.A.Greet()) // Hallo von A
fmt.Println(c.B.Greet()) // Hallo von B
}
Wenn Konflikte auftreten, musst du ausdrücklich angeben, die Methode welchen eingebetteten Typs du aufrufen möchtest, indem du den Typnamen als Qualifizierer verwendest.
Aufgabe
EinfachLass uns ein Smart-Home-Gerätesystem erstellen, das mithilfe der Einbettung von Structs mehrere Funktionen vereint. Du erstellst ein Gerät, das sowohl die Beleuchtung steuern als auch Musik abspielen kann, indem du separate Controller-Typen einbettest.
Du organisierst deinen Code in drei Dateien:
controllers.go: Erstelle zwei unabhängige Controller-Structs, die unterschiedliche Funktionen bereitstellen:- Einen
LightControllermit einem FeldBrightness(int) und einer MethodeSetLight(level int) string, dieLight set to [level]%zurückgibt - Einen
AudioControllermit einem FeldVolume(int) und einer MethodeSetVolume(level int) string, dieVolume set to [level]%zurückgibt
Status() stringhaben. Dadurch entsteht ein Namenskonflikt, den du behandeln musst.LightController.Status()sollteBrightness: [Brightness]%zurückgeben undAudioController.Status()sollteVolume: [Volume]%zurückgeben.- Einen
device.go: Erstelle ein StructSmartDevicemit einem FeldName, das sowohlLightControllerals auchAudioControllereinbettet. Füge eine Methode namensFullStatus() stringhinzu, die den Gerätenamen zusammen mit den Statusangaben beider Controller zurückgibt. Löse dabei die Mehrdeutigkeit, indem du die MethodeStatus()jedes eingebetteten Typs ausdrücklich aufrufst.main.go: Lies die Gerätekonfiguration aus der Eingabe, erstelle einSmartDevice, verwende die übernommenen Methoden, um die Licht- und Lautstärkestufen festzulegen, und gib dann den vollständigen Status aus.
Es werden die folgenden Eingaben bereitgestellt:
- Zeile 1: Gerätename
- Zeile 2: Anfangshelligkeit (Ganzzahl)
- Zeile 3: Anfangslautstärke (Ganzzahl)
- Zeile 4: Neue einzustellende Helligkeit (Ganzzahl)
- Zeile 5: Neue einzustellende Lautstärke (Ganzzahl)
Deine Methode FullStatus() sollte Folgendes zurückgeben:
[Name] - [LightController.Status()], [AudioController.Status()]Gib die Ergebnisse der Aufrufe von SetLight und SetVolume aus (diese werden von den eingebetteten Typen übernommen) und gib anschließend den vollständigen Status aus.
Wenn zum Beispiel Lounge, 50, 30, 75 und 60 angegeben werden, sollte deine Ausgabe so aussehen:
Light set to 75%
Volume set to 60%
Lounge - Brightness: 75%, Volume: 60%Beachte, dass SetLight und SetVolume dank Methodenübernahme direkt über SmartDevice zugänglich sind, während für Status() eine ausdrückliche Qualifizierung erforderlich ist, weil beide eingebetteten Typen diese Methode haben.
Probier es selbst
package main
import (
"fmt"
)
func main() {
// Eingaben einlesen
var name string
fmt.Scanln(&name)
var initialBrightness int
fmt.Scanln(&initialBrightness)
var initialVolume int
fmt.Scanln(&initialVolume)
var newBrightness int
fmt.Scanln(&newBrightness)
var newVolume int
fmt.Scanln(&newVolume)
// TODO: Ein SmartDevice mit dem angegebenen Namen und den Anfangswerten erstellen
// TODO: Die übernommene Methode SetLight verwenden und das Ergebnis ausgeben
// TODO: Die übernommene Methode SetVolume verwenden und das Ergebnis ausgeben
// TODO: Den vollständigen Status mit der Methode FullStatus() ausgeben
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler