©Sergey Emelyanov 2025 | Alle Rechte vorbehalten
In einem vorherigen Artikel haben wir das Builder-Pattern diskutiert, das die Konfiguration komplexer Objekte vereinfacht. Doch ein Nachteil blieb: Die Validierung von Parametern war erst im Build()-Schritt möglich, was Methodenketten unterbrach. Das Functional Options Pattern löst dieses Problem elegant und ist zudem ein idiomatischer Ansatz in Go.
Stellen Sie sich ein CRM-System vor, bei dem Kontakte mit optionalen Feldern wie Telefonnummer, E-Mail oder Firmendaten erstellt werden. Jedes Feld hat spezifische Validierungsregeln:
Ein Builder-Ansatz würde Validierungsfehler erst bei Build() werfen – zu spät für eine flüssige Methodenkette.
Das Pattern nutzt Closures und variadische Funktionen, um Optionen flexibel und mit sofortiger Validierung zu handhaben.
type Contact struct {
ID string
Name string
Phone string
Email string
Company *CompanyInfo
Address string
Notes string
}
type CompanyInfo struct {
Name string
Position string
}
type ContactOptions struct {
Phone string
Email string
Company *CompanyInfo
Address string
Notes string
}
type ContactOption func(*ContactOptions) error
Jede Option ist eine Funktion, die ContactOptions aktualisiert und sofort validiert:
func WithPhone(phone string) ContactOption {
return func(opts *ContactOptions) error {
if len(phone) < 10 {
return errors.New("Telefonnummer muss ≥10 Zeichen haben")
}
opts.Phone = phone
return nil
}
}
func WithEmail(email string) ContactOption {
return func(opts *ContactOptions) error {
_, err := mail.ParseAddress(email)
if err != nil {
return errors.New("Ungültige E-Mail-Adresse")
}
opts.Email = email
return nil
}
}
func WithCompany(name, position string) ContactOption {
return func(opts *ContactOptions) error {
if strings.TrimSpace(name) == "" || strings.TrimSpace(position) == "" {
return errors.New("Firmenname und Position sind erforderlich")
}
opts.Company = &CompanyInfo{Name: name, Position: position}
return nil
}
}
func CreateContact(id, name string, opts ...ContactOption) (*Contact, error) {
options := ContactOptions{}
for _, opt := range opts {
if err := opt(&options); err != nil {
return nil, err // Frühe Validierung!
}
}
return &Contact{
ID: id,
Name: name,
Phone: options.Phone,
Email: options.Email,
Company: options.Company,
Address: options.Address,
Notes: options.Notes,
}, nil
}
func main() {
contact, err := CreateContact(
"1",
"John Doe",
WithPhone("1234567890"),
WithEmail("john.doe@gmail.com"),
WithCompany("ABC Corp", "Manager"),
)
if err != nil {
fmt.Println("Fehler:", err)
return
}
fmt.Printf("Kontakt angelegt: %+v\n", contact)
}
Ausgabe:
Kontakt angelegt: &{ID:1 Name:John Doe Phone:1234567890 Email:john.doe@gmail.com Company:0xc0000ae040 Address: Notes:}
Das Pattern eignet sich ideal für:
Das Functional Options Pattern ist ein leistungsstarker Ansatz, um flexible und validierbare APIs in Go zu gestalten. Im Vergleich zum Builder-Pattern ermöglicht es frühe Fehlererkennung und bleibt dabei idiomatisch.
Weiterführende Optimierungen:
Mit diesem Pattern bleiben Ihre APIs wartbar, erweiterbar und developer-friendly.
©Sergey Emelyanov 2025 | Alle Rechte vorbehalten