Entwurfsmuster in Go: Wann und warum man sie verwenden sollte

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Entwurfsmuster in Go: Keine „Religion“, sondern ein Werkzeug

\n\n

Als ich anfing, in Go zu programmieren, verfolgte mich das Gefühl, dass all diese „Gang of Four“ und ihre 23 Muster etwas aus der Welt von Java und C++ seien. Go ist doch so einfach und prägnant – warum brauchen wir diese Komplexität? Aber je tiefer ich in reale Projekte eintauchte (Microservices, CLI-Tools, API-Handler), desto öfter ertappte ich mich bei dem Gedanken: „Hier wäre eine Factory-Methode nützlich“ oder „Diesen Berg von if-else könnte man durch eine Strategie ersetzen.“

\n\n

Entwurfsmuster in Go sind kein Dogma und kein Versuch, OOP auf eine funktionale Sprache zu übertragen. Es sind bewährte Lösungen für typische Probleme. Sie helfen dabei, Code testbar, flexibel und für Kollegen verständlich zu machen. Und ja, Go hinterlässt seine Spuren: Aufgrund des Fehlens klassischer Vererbung und Generics (vor Version 1.18) werden einige Muster transformiert, andere werden überflüssig.

\n\n

In diesem Artikel werden wir nicht alle 23 Muster auflisten. Stattdessen analysieren wir die 5 gefragtesten Vorlagen in Golang, die in echten Projekten wirklich nützlich sind. Sie werden sehen, wann man sie anwenden sollte und wann man besser die Finger davon lässt.

\n\n

Singleton-Muster in Go: Nicht nur ein Singleton, sondern ein Singleton auf Go-Art

\n\n

Singleton stellt sicher, dass es von einer Struktur nur eine Instanz gibt. In Go wird dies oft für Konfigurationen, Verbindungspools oder Logger verwendet. Es gibt jedoch eine Nuance: In Go gibt es keine statischen Klassen, dafür aber sync.Once – das ideale Werkzeug für faule Initialisierung.

\n\n

Problem ohne Singleton

\n

Stellen Sie sich vor, Sie haben eine Struktur Config, die Einstellungen aus einer Datei liest. Wenn jedes Paket seine eigene Instanz erstellt, lesen Sie die Datei entweder 10 Mal (langsam) oder erhalten eine Race-Condition.

\n\n

Lösung mit Singleton

\n
package config\n\nimport (\n    "sync"\n    "encoding/json"\n    "os"\n)\n\ntype Config struct {\n    Port int    `json:"port"`\n    Env  string `json:"env"`\n}\n\nvar (\n    instance *Config\n    once     sync.Once\n)\n\nfunc GetConfig() *Config {\n    once.Do(func() {\n        file, _ := os.Open("config.json")\n        defer file.Close()\n        instance = &Config{}\n        json.NewDecoder(file).Decode(instance)\n    })\n    return instance\n}
\n\n

Wann verwenden: wenn eine garantierte Einzigartigkeit der Instanz erforderlich ist (Konfigurationen, Caches, Fabriken).

\n

Wann nicht: für alles Mögliche. Ein Singleton ist ein globaler Zustand, der das Testen erschwert. Wenn Sie eine Abhängigkeit über den Konstruktor übergeben können – tun Sie das.

\n\n

Factory-Muster: Wenn die Objekterstellung komplex wird

\n\n

Factory Method und Abstract Factory dienen der Kapselung der Logik zur Objekterstellung. In Go ist dies besonders relevant, da wir keine Konstruktoren im klassischen Sinne haben. Stattdessen verwenden wir Konstruktorfunktionen mit dem Präfix New.

\n\n

Beispiel: Erstellung von Benachrichtigungen

\n

Angenommen, Ihr System sendet Benachrichtigungen per E-Mail, SMS und Push. Anstatt die Auswahllogik über den Code zu verteilen, verwenden wir eine Factory.

\n\n
package notification\n\nimport "fmt"\n\ntype Notifier interface {\n    Send(message string) error\n}\n\ntype EmailNotifier struct{}\n\nfunc (e EmailNotifier) Send(message string) error {\n    fmt.Println("Sending email:", message)\n    return nil\n}\n\ntype SMSNotifier struct{}\n\nfunc (s SMSNotifier) Send(message string) error {\n    fmt.Println("Sending SMS:", message)\n    return nil\n}\n\nfunc NewNotifier(typ string) Notifier {\n    switch typ {\n    case "email":\n        return EmailNotifier{}\n    case "sms":\n        return SMSNotifier{}\n    default:\n        return nil\n    }\n}
\n\n

Wann verwenden: wenn die Erstellungslogik komplex ist (abhängig von Typ, Version, Konfiguration).

\n

Wann nicht: wenn Sie nur einen Objekttyp haben. Schreiben Sie einfach MyStruct{} oder NewMyStruct() ohne Factory.

\n\n

Pa

Blogs

Buchempfehlungen