Einführung in Entwurfsmuster
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 85 von 107.
Entwurfsmuster sind wiederverwendbare Lösungen für häufig auftretende Probleme im Softwaredesign. Sie sind kein Code, den man direkt kopiert, sondern vielmehr Vorlagen, die anleiten, wie man seine Typen und deren Interaktionen strukturiert, um spezifische Herausforderungen zu lösen.
Design Patterns werden typischerweise in drei Kategorien unterteilt:
| Kategorie | Zweck | Beispiele |
|---|---|---|
| Erzeugungsmuster | Mechanismen zur Objekterzeugung | Singleton, Factory, Builder |
| Strukturmuster | Zusammensetzen von Typen zu größeren Strukturen | Adapter, Decorator, Composite |
| Verhaltensmuster | Kommunikation zwischen Objekten | Observer, Strategy, Command |
In Go sehen Entwurfsmuster anders aus als in traditionellen OOP-Sprachen.
Ohne Klassen oder Vererbung implementiert Go Muster mittels Interfaces, Struct-Embedding und Komposition. Zum Beispiel verwendet Go ein Interface, wo Java eine abstrakte Klasse verwenden würde. Wo C++ Vererbung nutzen würde, nutzt Go Embedding.
Das Erlernen von Patterns hilft Ihnen dabei, häufige Probleme zu erkennen und Lösungen effektiv mit anderen Entwicklern zu kommunizieren. Wenn jemand erwähnt: „Verwenden Sie hier das Strategy-Pattern“, versteht das gesamte Team den Ansatz ohne langwierige Erklärungen.
In den nächsten Lektionen werden Sie mehrere grundlegende Muster in Go implementieren, beginnend mit Erzeugungsmustern wie Singleton und Factory, gefolgt von Verhaltensmustern wie Observer und Strategy.
Aufgabe
EinfachLassen Sie uns ein einfaches Benachrichtigungssystem bauen, das zeigt, wie Entwurfsmuster helfen, Code zu organisieren! Sie werden eine Struktur erstellen, die in zukünftigen Lektionen leicht mit Mustern wie Observer oder Strategy erweitert werden könnte – aber konzentrieren Sie sich vorerst darauf, saubere Schnittstellen und Typen einzurichten, die guten OOP-Prinzipien folgen.
Sie werden Ihren Code in drei Dateien organisieren:
notifier.go: Definieren Sie das Kern-Interface und die Typen für Ihr Benachrichtigungssystem.Erstellen Sie ein
Notifier-Interface mit einer einzigen MethodeSend(message string) string, die eine Bestätigungsnachricht zurückgibt.Implementieren Sie zwei konkrete Benachrichtigungstypen:
EmailNotifiermit einemAddress-Feld – seineSend-Methode gibtEmail to [address]: [message]zurückSMSNotifiermit einemPhoneNumber-Feld – seineSend-Methode gibtSMS to [phone]: [message]zurück
dispatcher.go: Erstellen Sie einen Dispatcher, der über das Interface mit jedem Notifier zusammenarbeitet.Erstellen Sie ein
NotificationDispatcher-Struct, das ein Slice vonNotifier-Interfaces hält. Fügen Sie diese Methoden hinzu:AddNotifier(n Notifier), um einen Notifier zu registrierenBroadcast(message string) []string, das die Nachricht über alle registrierten Notifier sendet und ein Slice aller Bestätigungs-Strings zurückgibt
Erstellen Sie außerdem einen
NewNotificationDispatcher()-Konstruktor, der einen initialisierten Dispatcher zurückgibt.main.go: Richten Sie Notifier ein und senden Sie Nachrichten per Broadcast.Lesen Sie die Anzahl der zu erstellenden Notifier ein. Lesen Sie für jeden Notifier seinen Typ (
emailodersms) und sein Ziel (Adresse oder Telefonnummer) ein. Fügen Sie jeden einem Dispatcher hinzu.Lesen Sie dann eine Nachricht für den Broadcast ein. Senden Sie diese über alle Notifier und geben Sie jede Bestätigung in einer eigenen Zeile aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Anzahl der Notifier
- Für jeden Notifier: Typ (
emailodersms), dann das Ziel - Letzte Zeile: Nachricht für den Broadcast
Zum Beispiel, gegeben:
3
email
alice@example.com
sms
555-1234
email
bob@example.com
Server maintenance at midnightIhre Ausgabe sollte sein:
Email to alice@example.com: Server maintenance at midnight
SMS to 555-1234: Server maintenance at midnight
Email to bob@example.com: Server maintenance at midnightUnd gegeben:
2
sms
555-9999
sms
555-0000
Meeting canceledIhre Ausgabe sollte sein:
SMS to 555-9999: Meeting canceled
SMS to 555-0000: Meeting canceledDiese Struktur zeigt, wie Interfaces flexible und erweiterbare Designs ermöglichen. Der Dispatcher kennt keine spezifischen Notifier-Typen – er weiß nur, dass sie Send ausführen können. Dies ist die Grundlage, auf der Muster wie Observer und Strategy aufbauen!
Spickzettel
Design-Patterns sind wiederverwendbare Lösungen für häufig auftretende Probleme im Softwaredesign. Sie sind Vorlagen, die vorgeben, wie Typen und deren Interaktionen strukturiert werden sollen.
Design-Patterns werden in drei Kategorien unterteilt:
| Kategorie | Zweck | Beispiele |
|---|---|---|
| Erzeugungsmuster | Mechanismen zur Objekterzeugung | Singleton, Factory, Builder |
| Strukturmuster | Zusammensetzen von Typen zu größeren Strukturen | Adapter, Decorator, Composite |
| Verhaltensmuster | Kommunikation zwischen Objekten | Observer, Strategy, Command |
In Go werden Design-Patterns mithilfe von Interfaces, Struct-Embedding und Komposition anstelle von Klassen und Vererbung implementiert. Wo traditionelle OOP-Sprachen abstrakte Klassen verwenden, nutzt Go Interfaces. Wo diese Vererbung einsetzen, verwendet Go Embedding.
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Lies die Anzahl der Benachrichtiger
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
// Erstelle einen neuen Dispatcher
dispatcher := NewNotificationDispatcher()
// Lies jeden Benachrichtiger und füge ihn dem Dispatcher hinzu
for i := 0; i < count; i++ {
notifierType, _ := reader.ReadString('\n')
notifierType = strings.TrimSpace(notifierType)
destination, _ := reader.ReadString('\n')
destination = strings.TrimSpace(destination)
// TODO: Erstelle den entsprechenden Benachrichtiger basierend auf dem Typ
// und füge ihn dem Dispatcher hinzu
// Hinweis: Überprüfe, ob notifierType "email" oder "sms" ist
}
// Lies die zu sendende Nachricht
message, _ := reader.ReadString('\n')
message = strings.TrimSpace(message)
// TODO: Sende die Nachricht an alle und gib jede Bestätigung aus
}
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 Go OOP
Externe DateienGo Workspace & ModulePackages & ImportsExportierte vs. nicht-exportierte NamenEinführung in OOP in GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktor-FunktionenZusammenfassung – Einfacher Taschenrechner4Interfaces
Einführung in InterfacesImplizite ImplementierungInterface als VertragLeeres Interface (any)Type AssertionType SwitchInterface-KompositionStringer & Error InterfacesRückblick - Formen-Rechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf Paket-EbeneGetter- & Setter-MethodenInformation Hiding in GoZusammenfassung - Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterType ConstraintsGenerische StructsWorkaround für generische MethodenZusammenfassung – Generische Collection13Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterAbstract-Factory-MusterObserver-MusterStrategy-Muster2Vertiefung: Typen & Structs
Basis- & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct TagsAnonyme StructsVerschachtelte StructsZero Values & StandardwerteZusammenfassung - Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen des Struct EmbeddingMethod PromotionEinbetten mehrerer StructsEmbedding vs. AggregationShadowing eingebetteter MethodenZusammenfassung – Mitarbeiter-Hierarchie8Fehlerbehandlung & OOP
Das error-InterfaceEigene FehlertypenError Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverZusammenfassung – Datei-Parser3Zeiger & Speicher
Zeiger-Grundlagen in GoZeiger auf StructsWertübergabe vs. ReferenzübergabeDie new() FunktionGarbage Collection in GoWiederholung – Linked List Builder6Polymorphismus in Go
Polymorphismus über InterfacesDuck Typing in GoInterface-ErfüllungsregelnPolymorphe CollectionsDependency InjectionZusammenfassung – Payment Processor9Nebenläufigkeit & OOP
Goroutines GrundlagenChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignZusammenfassung - Worker Pool