GoLang Functional Options Pattern
Thi Functional Options Pattern allows for the complex configuration of some type. There are several layers of how involved this pattern can get and they are well described in a blog post by Rob Pike.
Basic form
Use case: Configuring a type with optional options.
Let’s say we want to create a server object that requires some options - databaseConnection - and may take some more options - host, port. Since GoLang does not support optional function parameters, but does support variadic arguments, this can be solved in a way that is at first gance overdone, but makes for very nice client side code:
type server struct {
databaseConnection *DatabaseConnection
host string
port int
}
type option func(*Server)
func NewServer(databaseConnection *DatabaseConnection, opts ...option) *server {
server := server{
databaseConnection: databaseConnection,
host: "0.0.0.0",
port: "42069",
}
for _, opt := range opts {
opt(server)
}
return &server
}
func WithHost(host string) option {
return func(server *server) {
server.host = host
}
}
func WithPort(port int) option {
return func(server *server) {
server.port = port
}
}
This allows the creation of a server like so:
server := NewServer(
databaseConnection,
WithHost("127.0.0.1"),
WithPort(80),
)
Since the Options at the end are variadic, they can be omitted at will.
Self-referential options
As Rob Pike describes in his blog post, these Options can be improved to make configuration changes temporary and automatically resettable. See his blog post for examples.
Without writing the concept up in its entirety, this is what the resulting API is, taken from the blog post and modified for readability:
func DoSomethingVerbosely(foo *Foo, verbosity int) {
previousVerbosityOption := foo.Option(pkg.Verbosity(verbosity))
defer foo.Option(previousVerbosityOption)
// ... do some stuff with foo under high verbosity.
}
Criticism
There is some valid criticism of this pattern. A good read is “Dysfunctional Options Pattern” by Redowan Delowar. He gives a good introduction into what the pattern is and what its use cases are, but also tells of its shortcomings and drawbacks, some more valid than others.