Menu
Coddy logo textTech

Polymorphismus über Interfaces

Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 41 von 107.

Polymorphismus ermöglicht es, verschiedene Typen über eine gemeinsame Schnittstelle einheitlich zu behandeln. In Go wird dies ausschließlich durch Interfaces erreicht, ohne Vererbung oder Klassenhierarchien.

Wenn eine Funktion einen Interface-Typ als Parameter akzeptiert, kann jeder konkrete Typ, der dieses Interface implementiert, übergeben werden. Die Funktion muss den spezifischen Typ nicht kennen – sie interessiert sich nur für das durch das Interface definierte Verhalten:

type Speaker interface {
    Speak() string
}

type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }

type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

Jetzt funktioniert MakeSound mit jedem Typ, der eine Speak()-Methode hat:

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Whiskers"}
    
    MakeSound(dog)  // Wuff!
    MakeSound(cat)  // Miau!
}

Derselbe Funktionsaufruf erzeugt unterschiedliches Verhalten, abhängig vom tatsächlich übergebenen Typ. Dies ist Polymorphismus in Aktion. Die Funktion MakeSound wird einmal geschrieben, funktioniert aber mit einer unbegrenzten Anzahl von Typen, solange diese das Interface Speaker erfüllen.

Dieser Ansatz hält Ihren Code flexibel und erweiterbar. Das Hinzufügen eines neuen Typs, der spricht, erfordert keine Änderungen an bestehenden Funktionen – implementieren Sie einfach das Interface, und es funktioniert automatisch.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Fahrzeugbeschreibungssystem erstellen, das Polymorphismus in Aktion demonstriert. Sie werden verschiedene Fahrzeugtypen erstellen, die alle ein gemeinsames Verhalten über ein Interface teilen, und dann eine einzelne Funktion schreiben, die mit jedem Fahrzeug funktioniert.

Sie werden Ihren Code auf zwei Dateien aufteilen:

  • vehicles.go: Definieren Sie ein Describer Interface, das eine Describe() string Methode erfordert. Erstellen Sie dann drei Fahrzeugtypen, die dieses Interface jeweils auf ihre eigene Weise implementieren:
    • Car mit den Feldern Brand und Model — dessen Describe() gibt Car: [Brand] [Model] zurück
    • Motorcycle mit den Feldern Brand und EngineCC (int) — dessen Describe() gibt Motorcycle: [Brand] [EngineCC]cc zurück
    • Bicycle mit dem Feld Type (wie "Mountain" oder "Road") — dessen Describe() gibt Bicycle: [Type] zurück
  • main.go: Erstellen Sie eine Funktion namens PrintDescription, die jeden Describer akzeptiert und das Ergebnis des Aufrufs von Describe() ausgibt. Lesen Sie Fahrzeugdetails aus der Eingabe ein, erstellen Sie von jedem Fahrzeugtyp eines und übergeben Sie jedes an PrintDescription, um zu demonstrieren, dass dieselbe Funktion mit allen drei verschiedenen Typen funktioniert.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Car brand
  • Zeile 2: Car model
  • Zeile 3: Motorcycle brand
  • Zeile 4: Motorcycle engine CC (Integer)
  • Zeile 5: Bicycle type

Zum Beispiel, gegeben Toyota, Camry, Honda, 600 und Mountain, sollte Ihre Ausgabe wie folgt aussehen:

Car: Toyota Camry
Motorcycle: Honda 600cc
Bicycle: Mountain

Beachten Sie, dass PrintDescription nicht wissen muss, ob es ein Car, Motorcycle oder Bicycle erhält — es ruft einfach Describe() auf und jeder Typ antwortet mit seiner eigenen, einzigartigen Ausgabe. Das ist Polymorphismus: eine Funktion, mehrere Verhaltensweisen.

Spickzettel

Polymorphismus ermöglicht es, verschiedene Typen durch eine gemeinsame Schnittstelle einheitlich zu behandeln. In Go wird dies durch Interfaces ohne Vererbung oder Klassenhierarchien erreicht.

Wenn eine Funktion einen Interface-Typ als Parameter akzeptiert, kann jeder konkrete Typ, der dieses Interface implementiert, übergeben werden:

type Speaker interface {
    Speak() string
}

type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }

type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

Dieselbe Funktion arbeitet mit verschiedenen Typen:

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Whiskers"}
    
    MakeSound(dog)  // Woof!
    MakeSound(cat)  // Meow!
}

Die Funktion wird einmal geschrieben, funktioniert aber mit unbegrenzten Typen, solange sie das Interface erfüllen. Das Hinzufügen neuer Typen erfordert keine Änderungen an bestehenden Funktionen – implementieren Sie einfach das Interface.

Probier es selbst

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// TODO: Erstelle eine Funktion namens PrintDescription, die einen beliebigen Describer akzeptiert
// und das Ergebnis des Aufrufs von Describe() ausgibt

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Autodetails lesen
	carBrand, _ := reader.ReadString('\n')
	carBrand = strings.TrimSpace(carBrand)
	carModel, _ := reader.ReadString('\n')
	carModel = strings.TrimSpace(carModel)

	// Motorraddetails lesen
	motoBrand, _ := reader.ReadString('\n')
	motoBrand = strings.TrimSpace(motoBrand)
	motoEngineStr, _ := reader.ReadString('\n')
	motoEngineStr = strings.TrimSpace(motoEngineStr)
	motoEngine, _ := strconv.Atoi(motoEngineStr)

	// Fahrraddetails lesen
	bicycleType, _ := reader.ReadString('\n')
	bicycleType = strings.TrimSpace(bicycleType)

	// TODO: Erstelle ein Car, Motorcycle und Bicycle unter Verwendung der Eingabewerte

	// TODO: Rufe PrintDescription für jedes Fahrzeug auf, um Polymorphismus zu demonstrieren
	fmt.Println("TODO: Print vehicle descriptions")
}
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