Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Lassen 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 einem Brand-Feld (string) und einer Play() string-Methode, die [Brand] playing audio zurückgibt
    • Ein Playable-Interface, das eine Play() string-Methode erfordert
    • Ein Smartphone-Struct mit einem Model-Feld, das AudioPlayer einbettet
    • Ein Tablet-Struct mit einem Model-Feld, das AudioPlayer einbettet
  • main.go: Erstellen Sie eine Funktion namens StartPlayback, die jedes Playable akzeptiert und das Ergebnis des Aufrufs von Play() zurückgibt. Lesen Sie Geräteinformationen aus der Eingabe ein, erstellen Sie sowohl ein Smartphone als auch ein Tablet und demonstrieren Sie, dass beide das Playable-Interface durch ihren eingebetteten AudioPlayer erfüllen – obwohl keiner der Typen Play() 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 audio

Beachten 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")
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung