Menu
Coddy logo textTech

Duck Typing in Go

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

Der Begriff Duck-Typing stammt von dem Sprichwort: „Wenn es wie eine Ente läuft und wie eine Ente quakt, dann ist es eine Ente.“ In Go bedeutet dies, dass ein Typ nicht explizit deklarieren muss, dass er ein Interface implementiert – er muss lediglich über die entsprechenden Methoden verfügen.

Betrachten Sie dieses Interface und zwei völlig voneinander unabhängige Typen:

type Quacker interface {
    Quack() string
}

type Duck struct{}
func (d Duck) Quack() string { return "Quack!" }

type Robot struct{}
func (r Robot) Quack() string { return "Beep-quack!" }

Weder Duck noch Robot erwähnen Quacker irgendwo in ihren Definitionen. Dennoch erfüllen beide das Interface einfach deshalb, weil sie eine Quack()-Methode mit der korrekten Signatur besitzen:

func MakeItQuack(q Quacker) {
    fmt.Println(q.Quack())
}

func main() {
    MakeItQuack(Duck{})   // Quack!
    MakeItQuack(Robot{})  // Beep-quack!
}

Diese implizite Erfüllung ist leistungsstark, da sie es Ihnen ermöglicht, Interfaces zu definieren, nachdem die Typen bereits existieren. Sie können ein Interface erstellen, das zu Typen aus externen Paketen passt, ohne deren Quellcode zu ändern. Die Typen müssen nichts über Ihr Interface wissen – sie müssen sich lediglich korrekt verhalten.

Duck-Typing in Go bietet Ihnen die Flexibilität dynamischer Sprachen, während die Typsicherheit zur Kompilierzeit erhalten bleibt. Der Compiler überprüft, ob Typen tatsächlich über die erforderlichen Methoden verfügen, bevor er zulässt, dass sie als Interface-Werte verwendet werden.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Nachrichtensystem bauen, das Duck-Typing in Aktion demonstriert. Sie werden völlig voneinander unabhängige Typen erstellen, die alle Nachrichten über eine gemeinsame Schnittstelle senden können – ohne dass einer von ihnen explizit deklariert, diese zu implementieren.

Sie werden Ihren Code über drei Dateien organisieren:

  • messengers.go: Erstellen Sie drei nicht verwandte Structs, die zufällig dasselbe Verhalten teilen:
    • Phone mit einem Number-Feld – seine SendMessage(text string) string-Methode gibt SMS to [Number]: [text] zurück
    • Computer mit einem Email-Feld – seine SendMessage(text string) string-Methode gibt Email to [Email]: [text] zurück
    • Pigeon mit einem Name-Feld – seine SendMessage(text string) string-Methode gibt [Name] carries: [text] zurück
    Beachten Sie, dass keiner dieser Typen ein Interface erwähnt – sie haben zufällig nur dieselbe Methodensignatur.
  • sender.go: Definieren Sie ein Messenger-Interface, das eine SendMessage(text string) string-Methode erfordert. Erstellen Sie eine Funktion namens Broadcast, die einen Slice von Messenger und einen Nachrichten-String entgegennimmt und dann einen Slice von Strings zurückgibt, der das Ergebnis des Aufrufs von SendMessage für jeden Messenger enthält.
  • main.go: Lesen Sie die Details aus der Eingabe ein, erstellen Sie jeweils einen der Messenger-Typen, sammeln Sie diese in einem Slice von Messenger und verwenden Sie Broadcast, um eine Nachricht über alle zu senden. Geben Sie jedes Ergebnis in einer eigenen Zeile aus.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Telefonnummer
  • Zeile 2: E-Mail-Adresse
  • Zeile 3: Name der Taube
  • Zeile 4: Zu sendende Nachricht

Zum Beispiel, gegeben 555-1234, alice@mail.com, Winston und Hello World, sollte Ihre Ausgabe wie folgt aussehen:

SMS to 555-1234: Hello World
Email to alice@mail.com: Hello World
Winston carries: Hello World

Die entscheidende Erkenntnis hierbei ist, dass Phone, Computer und Pigeon nichts gemeinsam haben – sie teilen keinen Basistyp und implementieren Messenger nicht explizit. Da sie jedoch alle auf die gleiche Weise „quaken“ (die passende Methode haben), erlaubt Go, dass sie alle als Messenger-Werte verwendet werden. Das ist Duck-Typing in der Praxis.

Spickzettel

In Go bedeutet Duck-Typing, dass ein Typ nicht explizit deklarieren muss, dass er ein Interface implementiert – er muss lediglich über die erforderlichen Methoden mit den entsprechenden Signaturen verfügen.

Ein Typ erfüllt ein Interface implizit, wenn er alle vom Interface geforderten Methoden besitzt:

type Quacker interface {
    Quack() string
}

type Duck struct{}
func (d Duck) Quack() string { return "Quack!" }

type Robot struct{}
func (r Robot) Quack() string { return "Beep-quack!" }

Sowohl Duck und Robot erfüllen das Quacker-Interface, ohne es in ihren Definitionen zu erwähnen:

func MakeItQuack(q Quacker) {
    fmt.Println(q.Quack())
}

func main() {
    MakeItQuack(Duck{})   // Quack!
    MakeItQuack(Robot{})  // Beep-quack!
}

Diese implizite Erfüllung ermöglicht es Ihnen, Interfaces zu definieren, nachdem Typen bereits existieren, und sogar Interfaces zu erstellen, die zu Typen aus externen Paketen passen, ohne deren Quellcode zu ändern. Der Compiler prüft zur Kompilierzeit, ob Typen über die erforderlichen Methoden verfügen, was Typsicherheit bietet und gleichzeitig Flexibilität bewahrt.

Probier es selbst

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Telefonnummer lesen
	scanner.Scan()
	phoneNumber := scanner.Text()
	
	// E-Mail-Adresse lesen
	scanner.Scan()
	email := scanner.Text()
	
	// Taubenname lesen
	scanner.Scan()
	pigeonName := scanner.Text()
	
	// Zu sendende Nachricht lesen
	scanner.Scan()
	message := scanner.Text()
	
	// TODO: Erstelle jeweils einen Messenger-Typ (Phone, Computer, Pigeon)
	
	// TODO: Sammle alle Messenger in einem Slice vom Typ Messenger
	
	// TODO: Verwende Broadcast, um die Nachricht über alle Messenger zu senden
	
	// TODO: Gib jedes Ergebnis in einer eigenen Zeile aus
	
	// Platzhalter zur Verwendung von Variablen (beim Implementieren entfernen)
	_ = phoneNumber
	_ = email
	_ = pigeonName
	_ = message
}
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