Understanding the Options Pattern in Go
Tags: webdev, go, programming, beginners
Published: 2023-08-02 14:16:57


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4i6aoi7n5vp6yiszrmpr.png)

## Options Pattern
The Options Pattern is a powerful and flexible design pattern commonly used in Go to configure and customize the behavior of a struct or function. This pattern is especially useful when you have a struct with numerous configuration parameters, and you want to provide a clean and readable way for users to set these parameters without cluttering the code with multiple constructor or function parameters.

### Let's dive into an example of how to implement the Options Pattern in Go:

Suppose we want to create a simple HTTP client that can be configured with various options like timeout, user agent, and whether to follow redirects. Instead of defining separate constructors or functions for each combination of options, we can use the Options Pattern to provide a cleaner and more flexible API.

```golang
package httpclient

import (
	"net/http"
	"time"
)

// Client represents our HTTP client.
type Client struct {
	client       *http.Client
	timeout      time.Duration
	userAgent    string
	followRedirects bool
}

// Option is a functional option type that allows us to configure the Client.
type Option func(*Client)

// NewClient creates a new HTTP client with default options.
func NewClient(options ...Option) *Client {
	client := &Client{
		client:       &http.Client{},
		timeout:      30 * time.Second, // Default timeout
		userAgent:    "My HTTP Client", // Default user agent
		followRedirects: true,         // Default follows redirects
	}

	// Apply all the functional options to configure the client.
	for _, opt := range options {
		opt(client)
	}

	return client
}

// WithTimeout is a functional option to set the HTTP client timeout.
func WithTimeout(timeout time.Duration) Option {
	return func(c *Client) {
		c.timeout = timeout
	}
}

// WithUserAgent is a functional option to set the HTTP client user agent.
func WithUserAgent(userAgent string) Option {
	return func(c *Client) {
		c.userAgent = userAgent
	}
}

// WithoutRedirects is a functional option to disable following redirects.
func WithoutRedirects() Option {
	return func(c *Client) {
		c.followRedirects = false
	}
}

// UseInsecureTransport is a functional option to use an insecure HTTP transport.
func UseInsecureTransport() Option {
	return func(c *Client) {
		c.client.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}
}

// Get performs an HTTP GET request.
func (c *Client) Get(url string) (*http.Response, error) {
	// Use c.client with all the configured options to perform the request.
	// ...
}

// Example usage:
func main() {
	// Create a new HTTP client with custom options.
	client := NewClient(
		WithTimeout(10 * time.Second),
		WithUserAgent("My Custom User Agent"),
		UseInsecureTransport(),
	)

	// Use the client to make HTTP requests.
	response, err := client.Get("https://api.example.com/data")
	if err != nil {
		// Handle the error
	}
	defer response.Body.Close()

	// Process the response.
	// ...
}
```
In this example, we have a Client struct that represents our HTTP client with its default options. We define a set of functional options (`Option type`) that modify the Client configuration. These options are then passed to the `NewClient` function, which applies them accordingly.

This approach allows users of the Client to easily customize its behavior without needing to specify all the parameters in the constructor. Additionally, it keeps the codebase clean, extensible, and readable.

## Conclusion 🥂
By using the Options Pattern, you can create more flexible and maintainable APIs for your Go packages and provide users with a convenient way to configure your structs or functions. As your project grows, you can easily add more options without changing the existing API, making it a great pattern for creating reusable and modular code.