Golang Functional Options pattern
Published on March 14, 2023
Functional options pattern implementation in Golang helps configure and initialize your structs expressive and flexible way.
person := NewPerson(
WithName("Alice"),
WithAge(21),
)
Define structure and constructor
First, describe your struct. There we will consider Person
struct with three fields.
// Person - describes some person
type Person struct {
Name string
Age int
Married bool
}
// PersonOption is option function type for Person type
type PersonOption func(person *Person)
// NewPerson - constructor for Person
func NewPerson(options ...PersonOption) Person {
// create instance with default values
person := Person{
Name: "Bob",
Age: 23,
Married: false,
}
for _, optionFunc := range options {
optionFunc(&person)
}
return person
}
Take closer look at the PersonOption
type, it gets only pointer for Person
instance and returns nothing. Getting
structure by pointer not by value is critical option functions. Every function type of PersonOption
can mutate given
Person
structure instance.
Second important part is constructor. Instead of getting certain values for person fields, constructor gets
an options slice. Constructor creates new instance of Person, iterate over given options and apply each options
to person instance.
Define options
// WithName - sets name for Person
func WithName(name string) PersonOption {
return func(person *Person) {
person.Name = name
}
}
The idea is simple - we define function returning option function. More detailed: WithName
func gets person name
as arguments, and returns new function. This new function has access to name argument as closure, and writes values
of argument to Name
field of person, when called.
Define additional options below:
// WithAge - sets age for Person
func WithAge(age int) PersonOption {
return func(person *Person) {
person.Age = age
}
}
// Married - sets Person as married
func Married() PersonOption {
return func(person *Person) {
person.Married = true
}
}
// Single - sets Person as single
func Single() PersonOption {
return func(person *Person) {
person.Married = false
}
}
Usage example:
person := NewPerson(
WithName("Alice"),
WithAge(21),
Single()
)
Conclusion
Now you can create instances of struct defining with parameters than you need only. Also you can to create more complex options feet to your needs. You can use one constructor method with different numbers of arguments. Option functions pattern helps you create flexible, expressive and easy to understand API.