Zusammensetzung von Schnittstellen
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 31 von 107.
Mit Go kannst du größere Schnittstellen erstellen, indem du kleinere zusammensetzt. Anstatt eine große Schnittstelle mit vielen Methoden zu definieren, bettest du vorhandene Schnittstellen in eine neue Schnittstellendefinition ein.
type Reader interface {
Read() string
}
type Writer interface {
Write(data string)
}
// ReadWriter kombiniert beide Schnittstellen
type ReadWriter interface {
Reader
Writer
}Die Schnittstelle ReadWriter erfordert jetzt sowohl die Methoden Read() als auch Write(). Jeder Typ, der beide Methoden implementiert, erfüllt ReadWriter sowie einzeln auch Reader und Writer.
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.
Aufgabe
EinfachErstellen wir ein Mediaplayer-System, das die Komposition von Schnittstellen demonstriert. Du erstellst kleine, fokussierte Schnittstellen und kombinierst sie anschließend zu einer leistungsfähigeren zusammengesetzten Schnittstelle, die ein einzelner Typ erfüllen kann.
Du organisierst deinen Code über zwei Dateien:
media.go: Definiere drei Schnittstellen und eine Struktur, die sie alle implementiert:- Eine
Player-Schnittstelle mit einerPlay() string-Methode - Eine
Recorder-Schnittstelle mit einerRecord(content string) string-Methode - Eine
MediaDevice-Schnittstelle, die sowohlPlayerals auchRecorderkombiniert - Eine
SmartDevice-Struktur mit einemName-Feld, die alle erforderlichen Methoden implementiert
- Eine
main.go: Erstelle Funktionen, die demonstrieren, wie dasselbeSmartDeviceüber verschiedene Schnittstellentypen verwendet werden kann. Schreibe drei Funktionen:UsePlayer, die einPlayer-Objekt akzeptiert und dessenPlay-Methode aufruftUseRecorder, die einRecorder-Objekt und eine Inhaltszeichenfolge akzeptiert und anschließendRecordaufruftUseMediaDevice, die einMediaDevice-Objekt und eine Inhaltszeichenfolge akzeptiert und anschließend sowohlRecordals auchPlayaufruft
SmartDeviceund demonstriere seine Verwendung über alle drei Schnittstellentypen.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Gerätename
- Zeile 2: Aufzuzeichnender Inhalt
Deine Methoden sollten Zeichenfolgen in diesen Formaten zurückgeben:
Play():[Name] is playingRecord(content):[Name] recorded: [content]
Wenn beispielsweise MyPhone und voice memo angegeben werden, sollte deine Ausgabe folgendermaßen aussehen:
MyPhone is playing
MyPhone recorded: voice memo
MyPhone recorded: voice memo
MyPhone is playingDie ersten beiden Zeilen zeigen das Gerät, das über die einzelnen Schnittstellen (Player und Recorder) verwendet wird. Die letzten beiden Zeilen zeigen seine Verwendung über die zusammengesetzte MediaDevice-Schnittstelle, die beide Fähigkeiten erfordert. Beachte, dass dein einzelnes SmartDevice alle drei Schnittstellen erfüllt, da es über beide erforderlichen Methoden verfügt.
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")
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler