Builder-Pattern in Go
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 80 von 107.
Das Builder-Pattern trennt die Konstruktion eines komplexen Objekts von seiner Darstellung. Im Gegensatz zum Functional-Options-Pattern verwendet der Builder ein dediziertes Builder-Struct mit Methoden, die das Objekt Schritt für Schritt konfigurieren.
So implementieren Sie 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-Pointer zurück, was Methodenverkettung ermöglicht. Die Build()-Methode schließt den Vorgang 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-Pattern zeichnet sich aus, wenn Objekte mehrere Konfigurationsschritte erfordern oder wenn Sie eine bestimmte Konstruktionsreihenfolge erzwingen möchten. Es ist besonders nützlich für die Erstellung unveränderlicher Objekte oder wenn der Konstruktionsprozess selbst eine Validierung erfordert.
Aufgabe
EinfachLassen Sie uns einen E-Mail-Nachrichten-Composer mit dem Builder-Pattern erstellen! E-Mail-Nachrichten haben viele optionale Komponenten – Empfänger, CC, Betreff, Textkörper, Anhänge – was sie zu einem perfekten Kandidaten für die schrittweise Konstruktion mit Method Chaining macht.
Sie werden Ihren Code auf zwei Dateien verteilen:
email.go: Definieren Sie Ihre E-Mail-Struktur und ihren Builder.Erstellen Sie ein
Email-Struct mit diesen Feldern:from(string)to(Slice von Strings)cc(Slice von Strings)subject(string)body(string)
Erstellen Sie ein
EmailBuilder-Struct, das einen Pointer auf eine im Bau befindliche E-Mail hält. Implementieren SieNewEmailBuilder(), das einen neuen Builder mit einer initialisierten E-Mail (leere Slices fürtoundcc) zurückgibt.Fügen Sie diese Builder-Methoden hinzu, die jeweils
*EmailBuilderfür das Chaining zurückgeben:From(address string)- setzt den AbsenderTo(address string)- fügt einen Empfänger zumto-Slice hinzuCC(address string)- fügt einen Empfänger zumcc-Slice hinzuSubject(subject string)- setzt die BetreffzeileBody(body string)- setzt den E-Mail-Textkörper
Fügen Sie eine
Build()-Methode hinzu, die die konstruierte*Emailzurückgibt.Fügen Sie schließlich eine
Summary()-Methode für 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: Erstellen und Anzeigen von E-Mail-Nachrichten mit dem Builder.Lesen Sie die Absenderadresse ein, lesen Sie dann die Anzahl der Empfänger und jede Empfängeradresse. Lesen Sie als Nächstes die Anzahl der CC-Empfänger und jede CC-Adresse. Lesen Sie schließlich den Betreff und den Textkörper ein.
Verwenden Sie den EmailBuilder mit Method Chaining, um die E-Mail zu konstruieren, und geben Sie dann deren 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: Textkörper
Zum Beispiel, gegeben:
alice@company.com
2
bob@company.com
carol@company.com
1
manager@company.com
Weekly Report
Please find the weekly report attached.Ihre Ausgabe sollte sein:
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 gegeben:
support@service.com
1
customer@email.com
0
Your Ticket Update
Your support ticket has been resolved.Ihre Ausgabe sollte sein:
From: support@service.com
To: customer@email.com
CC: none
Subject: Your Ticket Update
Body: Your support ticket has been resolved.Beachten Sie, wie der Builder es Ihnen ermöglicht, mehrere Empfänger nacheinander durch wiederholte To()-Aufrufe hinzuzufügen, und wie das Pattern den Konstruktionsprozess selbst bei vielen optionalen Feldern sauber und lesbar hält.
Spickzettel
Das Builder-Pattern trennt die Konstruktion eines komplexen Objekts von seiner Darstellung, indem es ein spezielles Builder-Struct mit Methoden verwendet, die das Objekt Schritt für Schritt konfigurieren.
Grundstruktur:
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),
},
}
}Builder-Methoden geben den Builder-Pointer zurück, um Method Chaining (Methodenverkettung) zu ermöglichen:
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
}Die Build()-Methode schließt den Vorgang ab und gibt das konstruierte Objekt zurück:
func (b *RequestBuilder) Build() *Request {
return b.request
}Verwendung mit Method Chaining:
req := NewRequestBuilder().
Method("POST").
URL("https://api.example.com").
Header("Content-Type", "application/json").
Build()Das Builder-Pattern ist nützlich, wenn Objekte mehrere Konfigurationsschritte erfordern, wenn eine bestimmte Konstruktionsreihenfolge erzwungen werden soll, zur Erstellung unveränderlicher (immutable) Objekte oder wenn der Konstruktionsprozess eine Validierung benötigt.
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)
// Textkörper lesen
body, _ := reader.ReadString('\n')
body = strings.TrimSpace(body)
// TODO: Verwenden Sie den EmailBuilder mit Methodenverkettung, um die E-Mail zu erstellen
// Beginnen Sie mit NewEmailBuilder(), verketten Sie dann From(), To(), CC(), Subject(), Body()
// Rufen Sie schließlich Build() auf, um die E-Mail zu erhalten
// TODO: Geben Sie die E-Mail-Zusammenfassung mit der Methode Summary() 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 Collection2Vertiefung: 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 Pool12Fortgeschrittene OOP-Konzepte
Functional Options PatternBuilder-Pattern in GoMethodenverkettungTyp-Aliase vs. DefinitionenReflection-GrundlagenÜberblick zur Code-Generierung