Menu
Coddy logo textTech

Implizite Implementierung

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

In vielen objektorientierten Sprachen wie Java oder C# müssen Sie explizit deklarieren, dass ein Typ ein Interface implementiert, indem Sie Schlüsselwörter wie implements verwenden. Go verfolgt einen anderen Ansatz: Die Interface-Implementierung erfolgt implizit.

Ein Typ erfüllt ein Interface einfach dadurch, dass er alle seine Methoden implementiert. Es ist nicht erforderlich, die Beziehung irgendwo zu deklarieren. Der Compiler findet dies automatisch heraus.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter implementiert Writer implizit
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

Beachten Sie, dass es keine implements Writer-Deklaration bei FileWriter gibt. Da FileWriter über eine Write(data string) int-Methode verfügt, die der Interface-Signatur entspricht, erfüllt es automatisch das Writer-Interface.

Dieses Design hat eine weitreichende Konsequenz: Sie können Interfaces definieren, nachdem Typen bereits existieren. Wenn eine Bibliothek eines Drittanbieters einen Typ mit Methoden hat, die Sie benötigen, können Sie ein Interface erstellen, das dieser bereits erfüllt, ohne den ursprünglichen Code zu ändern.

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // Funktioniert, da FileWriter Writer erfüllt
}

Implizite Implementierung hält Go-Code entkoppelt und flexibel. Typen müssen Interfaces nicht im Voraus kennen, was es einfacher macht, Systeme aus unabhängigen Teilen zusammenzusetzen.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Nachrichtenübermittlungssystem bauen, das demonstriert, wie Typen in Go Schnittstellen implizit erfüllen. Sie werden verschiedene Messenger-Typen erstellen, die alle dieselbe Schnittstelle erfüllen – ohne diese Beziehung jemals explizit zu deklarieren.

Sie werden Ihren Code auf zwei Dateien aufteilen:

  • messengers.go: Definieren Sie ein Messenger-Interface mit einer einzigen Methode Send(message string) string. Erstellen Sie dann zwei Structs, die dieses Interface implizit erfüllen werden:
    • EmailMessenger mit einem Address-Feld (string)
    • SMSMessenger mit einem PhoneNumber-Feld (string)
    Jedes Struct sollte eine Send-Methode haben, die einen String zurückgibt, der die Zustellung beschreibt. Keines der Structs sollte explizit deklarieren, dass es Messenger implementiert – Go wird das basierend auf den Methodensignaturen automatisch erkennen.
  • main.go: Erstellen Sie eine Funktion namens DeliverMessage, die einen beliebigen Messenger und einen Nachrichten-String akzeptiert und dann das Ergebnis des Aufrufs von Send zurückgibt. Lesen Sie Kontaktinformationen aus der Eingabe ein, erstellen Sie beide Messenger-Typen und verwenden Sie Ihre Funktion, um Nachrichten über jeden einzelnen zuzustellen.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: E-Mail-Adresse
  • Zeile 2: Telefonnummer
  • Zeile 3: Zu sendende Nachricht

Ihre Send-Methoden sollten Strings in diesen Formaten zurückgeben:

  • EmailMessenger: Email to [Address]: [message]
  • SMSMessenger: SMS to [PhoneNumber]: [message]

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

Email to alice@example.com: Hello!
SMS to 555-1234: Hello!

Die entscheidende Erkenntnis hierbei ist, dass Ihre DeliverMessage-Funktion jeden Messenger akzeptiert und sowohl EmailMessenger als auch SMSMessenger dieses Interface erfüllen, einfach indem sie eine passende Send-Methode besitzen. Es gibt nirgendwo ein implements-Schlüsselwort – Gos implizite Implementierung kümmert sich um alles.

Spickzettel

In Go erfolgt die Implementierung von Interfaces implizit. Ein Typ erfüllt ein Interface einfach dadurch, dass er alle seine Methoden implementiert – es ist keine explizite Deklaration erforderlich.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter implementiert Writer implizit
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

Es gibt keine implements Writer-Deklaration. Da FileWriter über eine Write(data string) int-Methode verfügt, die der Interface-Signatur entspricht, erfüllt er automatisch das Writer-Interface.

Sie können jeden Typ verwenden, der ein Interface erfüllt, ohne dass der Typ das Interface kennen muss:

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // Funktioniert, da FileWriter das Writer-Interface erfüllt
}

Dieses Design ermöglicht es Ihnen, Interfaces zu definieren, nachdem Typen bereits existieren, wodurch der Code entkoppelt und flexibel wird.

Probier es selbst

package main

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

// TODO: Erstellen Sie eine DeliverMessage-Funktion, die einen beliebigen Messenger
// und einen Nachrichten-String akzeptiert und dann das Ergebnis des Send-Aufrufs zurückgibt

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// E-Mail-Adresse lesen
	email, _ := reader.ReadString('\n')
	email = email[:len(email)-1]
	
	// Telefonnummer lesen
	phone, _ := reader.ReadString('\n')
	phone = phone[:len(phone)-1]
	
	// Nachricht lesen
	message, _ := reader.ReadString('\n')
	if len(message) > 0 && message[len(message)-1] == '\n' {
		message = message[:len(message)-1]
	}
	
	// TODO: Erstellen Sie einen EmailMessenger mit der E-Mail-Adresse
	
	// TODO: Erstellen Sie einen SMSMessenger mit der Telefonnummer
	
	// TODO: Verwenden Sie DeliverMessage, um die Nachricht über jeden Messenger zu senden
	// und die Ergebnisse auszugeben
}
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