Menu
Coddy logo textTech

Interface-Komposition

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

Go ermöglicht es Ihnen, größere Interfaces durch das Komponieren kleinerer zu bauen. Anstatt ein einziges großes Interface mit vielen Methoden zu definieren, betten Sie bestehende Interfaces in eine neue Interface-Definition ein.

type Reader interface {
    Read() string
}

type Writer interface {
    Write(data string)
}

// ReadWriter kombiniert beide Schnittstellen
type ReadWriter interface {
    Reader
    Writer
}

Das ReadWriter-Interface erfordert nun sowohl die Read()- als auch die Write()-Methode. Jeder Typ, der beide Methoden implementiert, erfüllt sowohl ReadWriter als auch Reader und Writer einzeln.

type File struct {
    Name string
}

func (f File) Read() string       { return "file content" }
func (f File) Write(data string)  { fmt.Println("Writing:", data) }

func Process(rw ReadWriter) {
    content := rw.Read()
    rw.Write(content)
}

func main() {
    f := File{Name: "data.txt"}
    Process(f)  // File erfüllt ReadWriter
}

Dieser Kompositionsansatz hält Interfaces klein und fokussiert. Sie können sie nach Bedarf kombinieren und so präzise Verträge für Ihre Funktionen erstellen. Die Standardbibliothek von Go nutzt dieses Muster ausgiebig, wie zum Beispiel io.ReadWriter, das io.Reader und io.Writer kombiniert.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Mediaplayer-System bauen, das die Komposition von Interfaces demonstriert. Sie werden kleine, fokussierte Interfaces erstellen und diese dann zu einem leistungsfähigeren, zusammengesetzten Interface kombinieren, das ein einzelner Typ erfüllen kann.

Sie werden Ihren Code in zwei Dateien organisieren:

  • media.go: Definieren Sie drei Interfaces und ein Struct, das sie alle implementiert:
    • Ein Player-Interface mit einer Play() string-Methode
    • Ein Recorder-Interface mit einer Record(content string) string-Methode
    • Ein MediaDevice-Interface, das sowohl Player als auch Recorder zusammensetzt
    • Ein SmartDevice-Struct mit einem Name-Feld, das alle erforderlichen Methoden implementiert
  • main.go: Erstellen Sie Funktionen, die demonstrieren, wie dasselbe SmartDevice über verschiedene Interface-Typen verwendet werden kann. Schreiben Sie drei Funktionen:
    • UsePlayer, die einen Player akzeptiert und dessen Play-Methode aufruft
    • UseRecorder, die einen Recorder und einen Inhalts-String akzeptiert und dann Record aufruft
    • UseMediaDevice, die ein MediaDevice und einen Inhalts-String akzeptiert und dann sowohl Record als auch Play aufruft
    Lesen Sie den Gerätenamen und den Inhalt aus der Eingabe ein, erstellen Sie ein SmartDevice und demonstrieren Sie dessen Verwendung über alle drei Interface-Typen.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Gerätename
  • Zeile 2: Aufzuzeichnender Inhalt

Ihre Methoden sollten Strings in diesen Formaten zurückgeben:

  • Play(): [Name] is playing
  • Record(content): [Name] recorded: [content]

Zum Beispiel, gegeben MyPhone und voice memo, sollte Ihre Ausgabe wie folgt aussehen:

MyPhone is playing
MyPhone recorded: voice memo
MyPhone recorded: voice memo
MyPhone is playing

Die ersten beiden Zeilen zeigen das Gerät, das über die einzelnen Interfaces (Player und Recorder) verwendet wird. Die letzten beiden Zeilen zeigen die Verwendung über das zusammengesetzte MediaDevice-Interface, das beide Fähigkeiten erfordert. Beachten Sie, wie Ihr einzelnes SmartDevice alle drei Interfaces erfüllt, da es beide erforderlichen Methoden besitzt.

Spickzettel

Go ermöglicht es Ihnen, größere Interfaces zu erstellen, indem Sie kleinere komponieren, indem Sie bestehende Interfaces in eine neue Interface-Definition einbetten:

type Reader interface {
    Read() string
}

type Writer interface {
    Write(data string)
}

// ReadWriter combines both interfaces
type ReadWriter interface {
    Reader
    Writer
}

Jeder Typ, der alle Methoden der eingebetteten Interfaces implementiert, erfüllt das komponierte Interface:

type File struct {
    Name string
}

func (f File) Read() string       { return "file content" }
func (f File) Write(data string)  { fmt.Println("Writing:", data) }

func Process(rw ReadWriter) {
    content := rw.Read()
    rw.Write(content)
}

func main() {
    f := File{Name: "data.txt"}
    Process(f)  // File satisfies ReadWriter
}

Ein Typ, der ein komponiertes Interface implementiert, erfüllt auch jedes der einzelnen eingebetteten Interfaces. Dieser Kompositionsansatz hält Interfaces klein und fokussiert, während er es Ihnen ermöglicht, präzise Verträge für Ihre Funktionen zu erstellen.

Probier es selbst

package main

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

// TODO: Implementiere UsePlayer, das einen Player akzeptiert und dessen Play-Methode aufruft
// Es sollte das Ergebnis von Play() ausgeben

// TODO: Implementiere UseRecorder, das einen Recorder und einen Inhalts-String akzeptiert
// Es sollte das Ergebnis von Record(content) ausgeben

// TODO: Implementiere UseMediaDevice, das ein MediaDevice und einen Inhalts-String akzeptiert
// Es sollte das Ergebnis von Record(content) und dann Play() ausgeben

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	scanner.Scan()
	deviceName := scanner.Text()
	
	scanner.Scan()
	content := scanner.Text()
	
	// TODO: Erstelle ein SmartDevice mit dem angegebenen Namen
	
	// TODO: Verwende das Gerät über das Player-Interface
	
	// TODO: Verwende das Gerät über das Recorder-Interface
	
	// TODO: Verwende das Gerät über das MediaDevice-Interface
	
	_ = deviceName
	_ = content
	fmt.Println("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