Menu
Coddy logo textTech

Schnittstelle als Vertrag

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

Schnittstellen als Verträge zu betrachten, hilft dir dabei, bessere Go-Programme zu entwerfen. Wenn eine Funktion einen Schnittstellentyp akzeptiert, sagt sie damit: „Es ist mir egal, welchen konkreten Typ du mir gibst, solange er diesen Vertrag erfüllt.“

Dieses vertragsbasierte Denken ermöglicht es dir, Funktionen zu schreiben, die sich auf das Verhalten statt auf bestimmte Typen konzentrieren. Betrachte eine Funktion, die Daten aus verschiedenen Quellen verarbeiten muss:

type DataSource interface {
    FetchData() string
}

func ProcessData(src DataSource) {
    data := src.FetchData()
    fmt.Println("Processing:", data)
}

Die Funktion ProcessData weiß nicht und kümmert sich nicht darum, ob sie eine Datenbankverbindung, einen API-Client oder einen Datei-Leser erhält. Sie benötigt lediglich, dass die Eingabe FetchData() aufrufen kann. Jeder Typ, der diesen Vertrag erfüllt, funktioniert.

type Database struct{ ConnectionString string }
func (d Database) FetchData() string { return "data from DB" }

type APIClient struct{ Endpoint string }
func (a APIClient) FetchData() string { return "data from API" }

func main() {
    db := Database{ConnectionString: "localhost"}
    api := APIClient{Endpoint: "https://api.example.com"}
    
    ProcessData(db)   // Funktioniert!
    ProcessData(api)  // Funktioniert auch!
}

Dieser Vertragsansatz macht deinen Code außerdem besser testbar. Während des Testens kannst du einen Mock-Typ erstellen, der die Schnittstelle erfüllt, ohne echte Datenbanken oder Netzwerkverbindungen zu benötigen. Die zu testende Funktion kennt den Unterschied nicht, weil der Vertrag erfüllt ist.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Benachrichtigungssystem, das zeigt, wie Schnittstellen als Verträge fungieren. Du erstellst eine Notifier-Schnittstelle, die definiert, was das Senden einer Benachrichtigung bedeutet, und implementierst anschließend mehrere Benachrichtigungskanäle, die alle diesen Vertrag einhalten.

Du organisierst deinen Code über zwei Dateien:

  • notifiers.go: Definiere eine Notifier-Schnittstelle mit einer einzigen Methode Notify(message string) string. Erstelle anschließend drei verschiedene Strukturen, die diesen Vertrag erfüllen:
    • ConsoleNotifier mit einem Prefix-Feld
    • FileNotifier mit einem Filename-Feld
    • WebhookNotifier mit einem URL-Feld
    Jede Struktur sollte die Methode Notify implementieren, um eine Zeichenkette zurückzugeben, die beschreibt, wie die Benachrichtigung gesendet würde.
  • main.go: Erstelle eine Funktion namens BroadcastAlert, die ein Slice von Notifier-Werten und eine Nachrichtenzeichenkette entgegennimmt. Diese Funktion sollte für jeden Notifier Notify aufrufen und die Ergebnisse ausgeben. Lies die Konfiguration aus der Eingabe, erstelle alle drei Notifier-Typen und sende eine Benachrichtigung über alle drei.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Präfix der Konsole
  • Zeile 2: Dateiname
  • Zeile 3: Webhook-URL
  • Zeile 4: Benachrichtigungstext

Deine Notify-Methoden sollten Zeichenketten in diesen Formaten zurückgeben:

  • ConsoleNotifier: [Prefix] [message]
  • FileNotifier: Writing to [Filename]: [message]
  • WebhookNotifier: POST [URL]: [message]

Wenn beispielsweise ALERT:, log.txt, https://hooks.example.com und Server down! gegeben sind, sollte deine Ausgabe folgendermaßen aussehen:

ALERT: Server down!
Writing to log.txt: Server down!
POST https://hooks.example.com: Server down!

Die Stärke dieses Entwurfs besteht darin, dass deine Funktion BroadcastAlert die spezifischen Notifier-Typen weder kennt noch sich um sie kümmern muss. Sie verlangt lediglich, dass jeder einzelne den Notifier-Vertrag erfüllt. Du könntest später einen SlackNotifier oder EmailNotifier hinzufügen, ohne die Broadcast-Funktion überhaupt zu ändern.

Probier es selbst

package main

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

// BroadcastAlert sendet eine Nachricht über alle Notifier
// TODO: Implementiere diese Funktion, um:
// - einen Slice von Notifier-Werten und einen Nachrichten-String zu akzeptieren
// - Notify auf jedem Notifier aufzurufen und die Ergebnisse auszugeben
func BroadcastAlert(notifiers []Notifier, message string) {
	// TODO: Gehe die Notifier durch und gib jedes Benachrichtigungsergebnis aus
}

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Konsolen-Präfix lesen
	scanner.Scan()
	prefix := scanner.Text()
	
	// Dateiname lesen
	scanner.Scan()
	filename := scanner.Text()
	
	// Webhook-URL lesen
	scanner.Scan()
	url := scanner.Text()
	
	// Alert-Nachricht lesen
	scanner.Scan()
	message := scanner.Text()
	
	// TODO: Erstelle Instanzen aller drei Notifier-Typen
	// - ConsoleNotifier mit dem Präfix
	// - FileNotifier mit dem Dateinamen
	// - WebhookNotifier mit der URL
	
	// TODO: Erstelle einen Slice von Notifier, der alle drei Notifier enthält
	
	// TODO: Rufe BroadcastAlert mit den Notifiers und der Nachricht auf
	
	_ = prefix
	_ = filename
	_ = url
	_ = message
	fmt.Println("TODO: Implement the solution")
}
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