Functional Options
A pattern for clean, extensible API design using variadic functions for optional configuration.
Contributors
When to Use
- When you have a struct with many optional fields
- When you want a clean and extensible API
- When default values need to be overridable
When NOT to Use
- For simple structs with few fields
- When configuration rarely changes
The Functional Options pattern is an idiomatic Go approach to constructing objects with many optional parameters. Instead of constructors with long parameter lists or builder patterns, it uses variadic functions that modify the object being created.
Implementation
package server
import "time"
type Server struct {
host string
port int
timeout time.Duration
maxConn int
}
// Option is a function that configures a Server.
type Option func(*Server)
// WithPort sets the server port.
func WithPort(port int) Option {
return func(s *Server) {
s.port = port
}
}
// WithTimeout sets the connection timeout.
func WithTimeout(timeout time.Duration) Option {
return func(s *Server) {
s.timeout = timeout
}
}
// WithMaxConnections sets the maximum number of connections.
func WithMaxConnections(max int) Option {
return func(s *Server) {
s.maxConn = max
}
}
// New creates a Server with the given host and options.
func New(host string, opts ...Option) *Server {
s := &Server{
host: host,
port: 8080, // default
timeout: 30 * time.Second, // default
maxConn: 100, // default
}
for _, opt := range opts {
opt(s)
}
return s
}
Usage
// Using all defaults
server := New("localhost")
// With custom options
server := New("localhost",
WithPort(9000),
WithTimeout(60 * time.Second),
WithMaxConnections(500),
)
Benefits
- Self-documenting: Each option function clearly states what it configures
- Extensible: New options can be added without breaking existing code
- Flexible defaults: Easy to provide sensible defaults that can be overridden
- Type-safe: The compiler catches invalid option usage
- Clean call sites: Only specify what you need to change