Einbetten mehrerer Structs
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 37 von 107.
Go ermöglicht es Ihnen, mehrere structs in ein einzelnes struct einzubetten und so Verhaltensweisen aus verschiedenen Quellen zu kombinieren. Dies ähnelt der Mehrfachvererbung in anderen Sprachen, jedoch mit dem Kompositionsansatz von Go.
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-Struct hat nun Zugriff auf Methoden beider eingebetteten Typen:
func main() {
s := Service{Name: "OrderService"}
fmt.Println(s.Log("started")) // LOG: started
fmt.Println(s.Notify("new order")) // NOTIFY: new order
}
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, müssen Sie explizit angeben, welche Methode des eingebetteten Typs Sie aufrufen möchten, indem Sie den Typnamen als Qualifizierer verwenden.
Aufgabe
EinfachLassen Sie uns ein Smart-Home-Gerätesystem bauen, das mehrere Funktionen durch Struct-Embedding kombiniert. Sie werden ein Gerät erstellen, das sowohl die Beleuchtung steuern als auch Musik abspielen kann, indem Sie separate Controller-Typen einbetten.
Sie werden Ihren Code über drei Dateien organisieren:
controllers.go: Erstellen Sie zwei unabhängige Controller-Structs, die unterschiedliche Funktionen bieten:- Ein
LightControllermit einemBrightness-Feld (int) und einerSetLight(level int) string-Methode, die"Light set to [level]%"zurückgibt. - Ein
AudioControllermit einemVolume-Feld (int) und einerSetVolume(level int) string-Methode, die"Volume set to [level]%"zurückgibt.
Status() string-Methode haben – dies erzeugt einen Namenskonflikt, den Sie behandeln müssen.LightController.Status()sollte"Brightness: [Brightness]%"zurückgeben undAudioController.Status()sollte"Volume: [Volume]%"zurückgeben.- Ein
device.go: Erstellen Sie einSmartDevice-Struct mit einemName-Feld, das sowohlLightControllerals auchAudioControllereinbettet. Fügen Sie eine Methode namensFullStatus() stringhinzu, die den Gerätenamen zusammen mit beiden Controller-Stati zurückgibt und die Mehrdeutigkeit auflöst, indem sie explizit dieStatus()-Methode jedes eingebetteten Typs aufruft.main.go: Lesen Sie die Gerätekonfiguration aus der Eingabe, erstellen Sie einSmartDevice, verwenden Sie die promoteten Methoden, um Licht- und Lautstärkepegel einzustellen, und zeigen Sie dann den vollständigen Status an.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Gerätename
- Zeile 2: Initialer Helligkeitswert (Integer)
- Zeile 3: Initialer Lautstärkewert (Integer)
- Zeile 4: Neuer einzustellender Helligkeitswert (Integer)
- Zeile 5: Neuer einzustellender Lautstärkewert (Integer)
Ihre FullStatus()-Methode sollte Folgendes zurückgeben:
[Name] - [LightController.Status()], [AudioController.Status()]Geben Sie die Ergebnisse des Aufrufs von SetLight und SetVolume aus (die von den eingebetteten Typen promotet werden), und geben Sie dann den vollständigen Status aus.
Zum Beispiel, gegeben "Living Room Hub", 50, 30, 75 und 60, sollte Ihre Ausgabe wie folgt aussehen:
Light set to 75%
Volume set to 60%
Living Room Hub - Brightness: 75%, Volume: 60%Beachten Sie, wie SetLight und SetVolume durch Method Promotion direkt auf SmartDevice zugänglich sind, aber Status() eine explizite Qualifizierung erfordert, da beide eingebetteten Typen diese Methode besitzen.
Spickzettel
Go ermöglicht das Einbetten mehrerer Structs in ein einzelnes Struct, wobei Verhaltensweisen aus verschiedenen Quellen durch Komposition kombiniert werden:
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
}
Das Service-Struct hat Zugriff auf Methoden beider eingebetteten Typen:
s := Service{Name: "OrderService"}
fmt.Println(s.Log("started")) // LOG: started
fmt.Println(s.Notify("new order")) // NOTIFY: new order
Namenskonflikte treten auf, wenn eingebettete Typen Methoden mit demselben Namen haben. Sie müssen explizit angeben, welche Methode des eingebetteten Typs aufgerufen werden soll:
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
}
// c.Greet() - Kompilierfehler: mehrdeutiger Selektor
c := Combined{}
fmt.Println(c.A.Greet()) // Hello from A
fmt.Println(c.B.Greet()) // Hello from B
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: Erstellen Sie ein SmartDevice mit dem angegebenen Namen und den Anfangswerten
// TODO: Verwenden Sie die heraufgestufte SetLight-Methode und geben Sie das Ergebnis aus
// TODO: Verwenden Sie die heraufgestufte SetVolume-Methode und geben Sie das Ergebnis aus
// TODO: Geben Sie den vollständigen Status mit der Methode FullStatus() 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 Go OOP
Externe DateienGo Workspace & ModulePackages & ImportsExportierte vs. nicht-exportierte NamenEinführung in OOP in GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktor-FunktionenZusammenfassung – Einfacher Taschenrechner4Interfaces
Einführung in InterfacesImplizite ImplementierungInterface als VertragLeeres Interface (any)Type AssertionType SwitchInterface-KompositionStringer & Error InterfacesRückblick - Formen-Rechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf Paket-EbeneGetter- & Setter-MethodenInformation Hiding in GoZusammenfassung - Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterType ConstraintsGenerische StructsWorkaround für generische MethodenZusammenfassung – Generische Collection2Vertiefung: Typen & Structs
Basis- & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct TagsAnonyme StructsVerschachtelte StructsZero Values & StandardwerteZusammenfassung - Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen des Struct EmbeddingMethod PromotionEinbetten mehrerer StructsEmbedding vs. AggregationShadowing eingebetteter MethodenZusammenfassung – Mitarbeiter-Hierarchie8Fehlerbehandlung & OOP
Das error-InterfaceEigene FehlertypenError Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverZusammenfassung – Datei-Parser3Zeiger & Speicher
Zeiger-Grundlagen in GoZeiger auf StructsWertübergabe vs. ReferenzübergabeDie new() FunktionGarbage Collection in GoWiederholung – Linked List Builder6Polymorphismus in Go
Polymorphismus über InterfacesDuck Typing in GoInterface-ErfüllungsregelnPolymorphe CollectionsDependency InjectionZusammenfassung – Payment Processor9Nebenläufigkeit & OOP
Goroutines GrundlagenChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignZusammenfassung - Worker Pool