Mehrere Structs einbetten
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 37 von 107.
Go ermöglicht es dir, mehrere Strukturen in eine einzelne Struktur einzubetten und Verhaltensweisen aus verschiedenen Quellen zu kombinieren. Dies ähnelt der Mehrfachvererbung in anderen Sprachen, verwendet jedoch Gos Kompositionsansatz.
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 Struktur Service hat nun Zugriff auf Methoden beider eingebetteten 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 Structs 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() - Kompilierfehler: mehrdeutiger Selektor
fmt.Println(c.A.Greet()) // Hello from A
fmt.Println(c.B.Greet()) // Hello from 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
EinfachWir erstellen ein Smart-Home-Gerätesystem, das durch das Einbetten von Structs mehrere Funktionen kombiniert. 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 über drei Dateien:
controllers.go: Erstelle zwei unabhängige Controller-Structs, die unterschiedliche Funktionen bereitstellen:- Einen
LightControllermit einemBrightness-Feld (int) und einerSetLight(level int) string-Methode, dieLight set to [level]%zurückgibt - Einen
AudioControllermit einemVolume-Feld (int) und einerSetVolume(level int) string-Methode, dieVolume set to [level]%zurückgibt
Status() string-Methode besitzen. Dadurch entsteht ein Namenskonflikt, den du behandeln musst.LightController.Status()sollteBrightness: [Brightness]%zurückgeben undAudioController.Status()sollteVolume: [Volume]%zurückgeben.- Einen
device.go: Erstelle einSmartDevice-Struct mit einemName-Feld, das sowohlLightControllerals auchAudioControllereinbettet. Füge eine Methode namensFullStatus() stringhinzu, die den Gerätenamen zusammen mit den Statusinformationen beider Controller zurückgibt, wobei du die Mehrdeutigkeit auflöst, indem du explizit dieStatus()-Methode jedes eingebetteten Typs aufrufst.main.go: Lies die Gerätekonfiguration aus der Eingabe, erstelle einSmartDevice, verwende die beförderten Methoden, um die Beleuchtungs- und Lautstärkepegel festzulegen, und zeige anschließend den vollständigen Status an.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Gerätename
- Zeile 2: Anfänglicher Beleuchtungspegel (Ganzzahl)
- Zeile 3: Anfänglicher Lautstärkepegel (Ganzzahl)
- Zeile 4: Neuer festzulegender Beleuchtungspegel (Ganzzahl)
- Zeile 5: Neuer festzulegender Lautstärkepegel (Ganzzahl)
Deine FullStatus()-Methode sollte Folgendes zurückgeben:
[Name] - [LightController.Status()], [AudioController.Status()]Gib die Ergebnisse der Aufrufe von SetLight und SetVolume aus (die von den eingebetteten Typen befördert werden), und gib anschließend den vollständigen Status aus.
Wenn beispielsweise Living Room Hub, 50, 30, 75 und 60 angegeben werden, sollte deine Ausgabe wie folgt aussehen:
Light set to 75%
Volume set to 60%
Living Room Hub - Brightness: 75%, Volume: 60%Beachte, dass SetLight und SetVolume durch Methodenbeförderung direkt auf SmartDevice zugänglich sind, während für Status() eine explizite Qualifizierung erforderlich ist, da beide eingebetteten Typen über diese Methode verfügen.
Probier es selbst
package main
import (
"fmt"
)
func main() {
// Eingabe lesen
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: Erstelle ein SmartDevice mit dem gegebenen Namen und den Anfangswerten
// TODO: Verwende die promovierte SetLight-Methode und gib das Ergebnis aus
// TODO: Verwende die promovierte SetVolume-Methode und gib das Ergebnis aus
// TODO: Gib den vollständigen Status mit der FullStatus()-Methode aus
}
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