Methoden-Promotion
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 36 von 107.
So wie die Einbettung von Structs Felder in das äußere Struct befördert, befördert Go auch Methoden aus eingebetteten Typen. Das bedeutet, dass du Methoden, die für das eingebettete Struct definiert sind, direkt für das äußere Struct aufrufen kannst.
type Engine struct {
Horsepower int
}
func (e Engine) Start() string {
return "Engine started"
}
type Car struct {
Model string
Engine // eingebettet
}
Da Car Engine einbettet, wird die Start()-Methode automatisch weitergereicht:
func main() {
c := Car{
Model: "Sedan",
Engine: Engine{Horsepower: 200},
}
fmt.Println(c.Start()) // Motor gestartet
fmt.Println(c.Engine.Start()) // funktioniert auch
}
Diese Einbettung hat eine wichtige Konsequenz für Schnittstellen. Wenn der eingebettete Typ eine Schnittstelle erfüllt, erfüllt der äußere Typ sie 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 ausdrücklich, erfüllt jedoch die Schnittstelle, weil sein eingebettetes Engine über die erforderliche Methode verfügt. So erreicht Go die Wiederverwendung von Verhalten ohne traditionelle Vererbung.
Aufgabe
EinfachErstellen wir ein Musikplayer-System, das veranschaulicht, wie Methoden eingebetteter Typen an den äußeren Typ weitergereicht werden und wie dadurch die Erfüllung eines Interfaces durch Komposition ermöglicht wird.
Du organisierst deinen Code über zwei Dateien:
audio.go: Erstelle die Bausteine für deinen Musikplayer:- Eine
AudioPlayer-Struktur mit einemBrand-Feld (String) und einerPlay() string-Methode, die[Brand] playing audiozurückgibt - Ein
Playable-Interface, das einePlay() string-Methode erfordert - Eine
Smartphone-Struktur mit einemModel-Feld, dieAudioPlayereinbettet - Eine
Tablet-Struktur mit einemModel-Feld, dieAudioPlayereinbettet
- Eine
main.go: Erstelle eine Funktion namensStartPlayback, die ein beliebigesPlayableakzeptiert und das Ergebnis des Aufrufs vonPlay()zurückgibt. Lies die Geräteinformationen aus der Eingabe ein, erstelle sowohl ein Smartphone als auch ein Tablet und zeige, dass beide über ihren eingebettetenPlayabledasAudioPlayer-Interface erfüllen, obwohl keiner der beiden TypenPlay()selbst explizit implementiert.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Modellname des Smartphones
- Zeile 2: Audiomarke des Smartphones
- Zeile 3: Modellname des Tablets
- Zeile 4: Audiomarke des Tablets
Gib für jedes Gerät sein Modell aus, gefolgt vom Ergebnis der Übergabe an StartPlayback.
Wenn beispielsweise iPhone 15, Apple Audio, iPad Pro und Beats gegeben sind, sollte deine Ausgabe wie folgt aussehen:
iPhone 15
Apple Audio playing audio
iPad Pro
Beats playing audioBeachte, dass sowohl Smartphone als auch Tablet an StartPlayback übergeben werden können, weil die Play()-Methode von AudioPlayer automatisch an jeden äußeren Typ weitergereicht wird und dadurch das Playable-Interface ohne zusätzlichen Code erfüllt.
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 einlesen
scanner.Scan()
smartphoneModel := scanner.Text()
// Smartphone-Audio-Marke einlesen
scanner.Scan()
smartphoneBrand := scanner.Text()
// Tablet-Modell einlesen
scanner.Scan()
tabletModel := scanner.Text()
// Tablet-Audio-Marke einlesen
scanner.Scan()
tabletBrand := scanner.Text()
// TODO: Erstelle ein Smartphone mit dem eingebetteten AudioPlayer
// TODO: Erstelle ein Tablet mit dem eingebetteten AudioPlayer
// TODO: Gib das Smartphone-Modell aus und rufe dann StartPlayback mit dem Smartphone auf
// TODO: Gib das Tablet-Modell aus und rufe dann StartPlayback mit dem Tablet auf
_ = 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 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