Method Promotion
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 36 von 107.
Genauso wie das Einbetten von Structs Felder in den äußeren Struct befördert, befördert Go auch Methoden von eingebetteten Typen. Das bedeutet, dass Sie Methoden, die für den eingebetteten Struct definiert sind, direkt auf dem äußeren Struct aufrufen können.
type Engine struct {
Horsepower int
}
func (e Engine) Start() string {
return "Engine started"
}
type Car struct {
Model string
Engine // eingebettet
}
Da Car den Typ Engine einbettet, wird die Start()-Methode automatisch hochgestuft:
func main() {
c := Car{
Model: "Sedan",
Engine: Engine{Horsepower: 200},
}
fmt.Println(c.Start()) // Engine gestartet
fmt.Println(c.Engine.Start()) // funktioniert ebenfalls
}
Diese Promotion hat eine wichtige Konsequenz für Interfaces. Wenn der eingebettete Typ ein Interface erfüllt, erfüllt der äußere Typ dieses automatisch ebenfalls:
type Starter interface {
Start() string
}
func Ignite(s Starter) {
fmt.Println(s.Start())
}
func main() {
c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
Ignite(c) // Car erfüllt Starter durch Engine
}
Der Typ Car implementiert Starter nie explizit, erfüllt jedoch das Interface, da sein eingebettetes Engine über die erforderliche Methode verfügt. So erreicht Go die Wiederverwendung von Verhalten ohne traditionelle Vererbung.
Aufgabe
EinfachLassen Sie uns ein Musik-Player-System bauen, das demonstriert, wie Methoden von eingebetteten Typen an den äußeren Typ weitergegeben (promoted) werden und wie dies die Erfüllung von Interfaces durch Komposition ermöglicht.
Sie werden Ihren Code über zwei Dateien organisieren:
audio.go: Erstellen Sie die Bausteine für Ihren Musik-Player:- Ein
AudioPlayer-Struct mit einemBrand-Feld (string) und einerPlay() string-Methode, die[Brand] playing audiozurückgibt - Ein
Playable-Interface, das einePlay() string-Methode erfordert - Ein
Smartphone-Struct mit einemModel-Feld, dasAudioPlayereinbettet - Ein
Tablet-Struct mit einemModel-Feld, dasAudioPlayereinbettet
- Ein
main.go: Erstellen Sie eine Funktion namensStartPlayback, die jedesPlayableakzeptiert und das Ergebnis des Aufrufs vonPlay()zurückgibt. Lesen Sie Geräteinformationen aus der Eingabe ein, erstellen Sie sowohl ein Smartphone als auch ein Tablet und demonstrieren Sie, dass beide dasPlayable-Interface durch ihren eingebettetenAudioPlayererfüllen – obwohl keiner der TypenPlay()selbst explizit implementiert.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Smartphone-Modellname
- Zeile 2: Smartphone-Audio-Marke
- Zeile 3: Tablet-Modellname
- Zeile 4: Tablet-Audio-Marke
Geben Sie für jedes Gerät sein Modell gefolgt von dem Ergebnis der Übergabe an StartPlayback aus.
Zum Beispiel, gegeben iPhone 15, Apple Audio, iPad Pro und Beats, sollte Ihre Ausgabe wie folgt aussehen:
iPhone 15
Apple Audio playing audio
iPad Pro
Beats playing audioBeachten Sie, wie sowohl Smartphone als auch Tablet an StartPlayback übergeben werden können, da die Play()-Methode von AudioPlayer automatisch an jeden äußeren Typ weitergegeben wird und so das Playable-Interface ohne zusätzlichen Code erfüllt.
Spickzettel
Wenn ein Struct einen anderen Typ einbettet, werden Methoden des eingebetteten Typs automatisch an das äußere Struct weitergereicht (promoted):
type Engine struct {
Horsepower int
}
func (e Engine) Start() string {
return "Engine started"
}
type Car struct {
Model string
Engine // eingebettet
}
func main() {
c := Car{
Model: "Sedan",
Engine: Engine{Horsepower: 200},
}
fmt.Println(c.Start()) // Engine started
fmt.Println(c.Engine.Start()) // funktioniert ebenfalls
}
Wenn ein eingebetteter Typ ein Interface erfüllt, erfüllt der äußere Typ dieses automatisch ebenfalls:
type Starter interface {
Start() string
}
func Ignite(s Starter) {
fmt.Println(s.Start())
}
func main() {
c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
Ignite(c) // Car erfüllt Starter durch Engine
}
Dies ermöglicht die Wiederverwendung von Verhalten durch Komposition ohne traditionelle Vererbung.
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
)
// TODO: Erstelle eine StartPlayback-Funktion, die jedes Playable akzeptiert
// und das Ergebnis des Aufrufs von Play() zurückgibt
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Smartphone-Modell lesen
scanner.Scan()
smartphoneModel := scanner.Text()
// Smartphone-Audiomarke lesen
scanner.Scan()
smartphoneBrand := scanner.Text()
// Tablet-Modell lesen
scanner.Scan()
tabletModel := scanner.Text()
// Tablet-Audiomarke lesen
scanner.Scan()
tabletBrand := scanner.Text()
// TODO: Erstelle ein Smartphone mit dem eingebetteten AudioPlayer
// TODO: Erstelle ein Tablet mit dem eingebetteten AudioPlayer
// TODO: Smartphone-Modell ausgeben, dann StartPlayback mit dem Smartphone aufrufen
// TODO: Tablet-Modell ausgeben, dann StartPlayback mit dem Tablet aufrufen
_ = smartphoneModel
_ = smartphoneBrand
_ = tabletModel
_ = tabletBrand
fmt.Println("TODO: Complete the implementation")
}
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