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 aus dem Sprichwort: „Wenn es wie eine Ente läuft und wie eine Ente quakt, dann ist es eine Ente.“ In Go bedeutet das, dass ein Typ nicht ausdrücklich deklarieren muss, dass er ein Interface implementiert. Er muss lediglich über die richtigen Methoden verfügen.

Betrachte diese Schnittstelle und zwei völlig 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ähnt Quacker irgendwo in ihren Definitionen. Dennoch erfüllen beide die Schnittstelle, einfach weil sie über eine Quack()-Methode mit der korrekten Signatur verfügen:

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

func main() {
    MakeItQuack(Duck{})   // Quak!
    MakeItQuack(Robot{})  // Piep-Quak!
}

Diese implizite Erfüllung ist leistungsstark, weil sie es dir ermöglicht, Schnittstellen zu definieren, nachdem die Typen bereits existieren. Du kannst eine Schnittstelle erstellen, die Typen aus externen Paketen entspricht, ohne deren Quellcode zu ändern. Die Typen müssen nichts von deiner Schnittstelle wissen. Sie müssen sich nur korrekt verhalten.

Duck-Typing in Go bietet dir die Flexibilität dynamischer Sprachen und bewahrt gleichzeitig die Typsicherheit zur Compile-Zeit. 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

Erstellen wir ein Nachrichtensystem, das Duck-Typing in der Praxis demonstriert. Du erstellst vollständig unabhängige Typen, die alle Nachrichten über eine gemeinsame Schnittstelle senden können, ohne dass einer von ihnen ausdrücklich angibt, diese zu implementieren.

Du organisierst deinen Code über drei Dateien:

  • messengers.go: Erstelle drei unabhängige Structs, die zufällig dasselbe Verhalten aufweisen:
    • Phone mit einem Feld Number. Seine Methode SendMessage(text string) string gibt SMS to [Number]: [text] zurück
    • Computer mit einem Feld Email. Seine Methode SendMessage(text string) string gibt Email to [Email]: [text] zurück
    • Pigeon mit einem Feld Name. Seine Methode SendMessage(text string) string gibt [Name] carries: [text] zurück
    Beachte, dass keiner dieser Typen eine Schnittstelle erwähnt. Sie haben einfach zufällig dieselbe Methodensignatur.
  • sender.go: Definiere eine Messenger-Schnittstelle, die eine SendMessage(text string) string-Methode erfordert. Erstelle eine Funktion namens Broadcast, die ein Slice von Messenger und einen Nachrichten-String entgegennimmt und anschließend ein Slice von Strings zurückgibt, das die Ergebnisse des Aufrufs von SendMessage für jeden Messenger enthält.
  • main.go: Lies die Details aus der Eingabe ein, erstelle jeweils eine Instanz jedes Messenger-Typs, fasse sie in einem Slice von Messenger zusammen und verwende Broadcast, um eine Nachricht über alle zu senden. Gib 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 verbreitende Nachricht

Wenn beispielsweise 555-1234, alice@mail.com, Winston und Hello World angegeben werden, sollte deine Ausgabe wie folgt aussehen:

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

Die wichtigste Erkenntnis hierbei ist, dass Phone, Computer und Pigeon nichts gemeinsam haben. Sie teilen keinen Basistyp und implementieren Messenger nicht ausdrücklich. Da sie sich jedoch alle auf dieselbe Weise verhalten (über die passende Methode verfügen), erlaubt Go, sie alle als Messenger-Werte zu verwenden. Das ist Duck-Typing in der Praxis.

Probier es selbst

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Telefonnummer lesen
	scanner.Scan()
	phoneNumber := scanner.Text()
	
	// Read email address
	scanner.Scan()
	email := scanner.Text()
	
	// Taubennamen lesen
	scanner.Scan()
	pigeonName := scanner.Text()
	
	// Zu sendende Nachricht lesen
	scanner.Scan()
	message := scanner.Text()
	
	// TODO: Erstelle einen von jedem Messenger-Typ (Phone, Computer, Pigeon)
	
	// TODO: Sammle alle Messenger in einem Slice von Messenger
	
	// TODO: Verwende Broadcast, um die Nachricht über alle Messenger zu senden
	
	// TODO: Gib jedes Ergebnis in einer eigenen Zeile aus
	
	// Platzhalter zur Verwendung der 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

Übe selbstständig: Online-Go-Compiler