Builder-Pattern in Go
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 80 von 107.
Das Builder-Muster trennt die Erstellung eines komplexen Objekts von seiner Darstellung. Im Gegensatz zum Muster der funktionalen Optionen verwendet der Builder eine spezielle Builder-Struktur mit Methoden, die das Objekt Schritt für Schritt konfigurieren.
So implementierst du einen Builder für eine HTTP-Anfrage:
type Request struct {
method string
url string
headers map[string]string
body string
}
type RequestBuilder struct {
request *Request
}
func NewRequestBuilder() *RequestBuilder {
return &RequestBuilder{
request: &Request{
headers: make(map[string]string),
},
}
}
func (b *RequestBuilder) Method(m string) *RequestBuilder {
b.request.method = m
return b
}
func (b *RequestBuilder) URL(u string) *RequestBuilder {
b.request.url = u
return b
}
func (b *RequestBuilder) Header(key, value string) *RequestBuilder {
b.request.headers[key] = value
return b
}
func (b *RequestBuilder) Build() *Request {
return b.request
}Jede Methode gibt den Builder-Zeiger zurück und ermöglicht dadurch das Verketten von Methoden. Die Methode Build() schließt die Erstellung ab und gibt das erstellte Objekt zurück:
req := NewRequestBuilder().
Method("POST").
URL("https://api.example.com").
Header("Content-Type", "application/json").
Build()Das Builder-Muster eignet sich hervorragend, wenn Objekte mehrere Konfigurationsschritte erfordern oder wenn du eine bestimmte Konstruktionsreihenfolge erzwingen möchtest. Es ist besonders nützlich zum Erstellen unveränderlicher Objekte oder wenn der Konstruktionsprozess selbst eine Validierung benötigt.
Aufgabe
EinfachErstellen wir einen E-Mail-Nachrichten-Composer mit dem Builder Pattern! E-Mail-Nachrichten haben viele optionale Bestandteile – Absender, Empfänger, CC, Betreff, Text und Anhänge – und eignen sich daher perfekt für eine schrittweise Erstellung mit Methodenverkettung.
Du organisierst deinen Code über zwei Dateien:
email.go: Definiere deine E-Mail-Struktur und ihren Builder.Erstelle eine
Email-Struktur mit diesen Feldern:from(String)to(String-Slice)cc(String-Slice)subject(String)body(String)
Erstelle eine
EmailBuilder-Struktur, die einen Zeiger auf eine gerade erstellte Email enthält. ImplementiereNewEmailBuilder(), die einen neuen Builder mit einer initialisierten Email zurückgibt (leere Slices fürtoundcc).Füge diese Builder-Methoden hinzu, die jeweils
*EmailBuilderfür die Verkettung zurückgeben:From(address string)– legt den Absender festTo(address string)– fügt derto-Slice einen Empfänger hinzuCC(address string)– fügt dercc-Slice einen Empfänger hinzuSubject(subject string)– legt die Betreffzeile festBody(body string)– legt den E-Mail-Text fest
Füge eine
Build()-Methode hinzu, die die erstellte*Emailzurückgibt.Füge schließlich eine
Summary()-Methode zu Email hinzu, die einen String in diesem Format zurückgibt:From: [from] To: [comma-separated to addresses] CC: [comma-separated cc addresses or "none"] Subject: [subject] Body: [body]main.go: Erstelle E-Mail-Nachrichten mit dem Builder und zeige sie an.Lies die Absenderadresse ein, dann die Anzahl der Empfänger und jede Empfängeradresse. Lies anschließend die Anzahl der CC-Empfänger und jede CC-Adresse ein. Lies zum Schluss den Betreff und den Text ein.
Verwende den EmailBuilder mit Methodenverkettung, um die E-Mail zu erstellen, und gib anschließend ihre Zusammenfassung aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Absenderadresse
- Zeile 2: Anzahl der Empfänger
- Folgende Zeilen: Jede Empfängeradresse
- Nächste Zeile: Anzahl der CC-Empfänger
- Folgende Zeilen: Jede CC-Adresse
- Nächste Zeile: Betreff
- Letzte Zeile: Text
Zum Beispiel bei folgender Eingabe:
alice@company.com
2
bob@company.com
carol@company.com
1
manager@company.com
Weekly Report
Please find the weekly report attached.Deine Ausgabe sollte folgendermaßen aussehen:
From: alice@company.com
To: bob@company.com, carol@company.com
CC: manager@company.com
Subject: Weekly Report
Body: Please find the weekly report attached.Und bei folgender Eingabe:
support@service.com
1
customer@email.com
0
Your Ticket Update
Your support ticket has been resolved.Deine Ausgabe sollte folgendermaßen aussehen:
From: support@service.com
To: customer@email.com
CC: none
Subject: Your Ticket Update
Body: Your support ticket has been resolved.Beachte, wie der Builder dir ermöglicht, durch wiederholte Aufrufe von To() mehrere Empfänger einzeln hinzuzufügen, und wie das Muster den Erstellungsprozess auch bei vielen optionalen Feldern übersichtlich und gut lesbar hält.
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Absenderadresse lesen
from, _ := reader.ReadString('\n')
from = strings.TrimSpace(from)
// Anzahl der Empfänger lesen
toCountStr, _ := reader.ReadString('\n')
toCount, _ := strconv.Atoi(strings.TrimSpace(toCountStr))
// Jede Empfängeradresse lesen
toAddresses := make([]string, toCount)
for i := 0; i < toCount; i++ {
addr, _ := reader.ReadString('\n')
toAddresses[i] = strings.TrimSpace(addr)
}
// Anzahl der CC-Empfänger lesen
ccCountStr, _ := reader.ReadString('\n')
ccCount, _ := strconv.Atoi(strings.TrimSpace(ccCountStr))
// Jede CC-Adresse lesen
ccAddresses := make([]string, ccCount)
for i := 0; i < ccCount; i++ {
addr, _ := reader.ReadString('\n')
ccAddresses[i] = strings.TrimSpace(addr)
}
// Betreff lesen
subject, _ := reader.ReadString('\n')
subject = strings.TrimSpace(subject)
// Nachrichtentext lesen
body, _ := reader.ReadString('\n')
body = strings.TrimSpace(body)
// TODO: Verwende den EmailBuilder mit Method Chaining, um die E-Mail zu erstellen
// Beginne mit NewEmailBuilder(), dann verketten From(), To(), CC(), Subject(), Body()
// Rufe abschließend Build() auf, um die Email zu erhalten
// TODO: Gib die E-Mail-Zusammenfassung mit der Summary()-Methode 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 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 Pool12Fortgeschrittene OOP-Konzepte
Functional-Options-PatternBuilder-Pattern in GoMethod ChainingTyp-Aliase vs. DefinitionenGrundlagen der ReflectionÜberblick über CodegenerierungÜbe selbstständig: Online-Go-Compiler