{"id":21423249,"url":"https://github.com/stevencyb/golang-functional-options","last_synced_at":"2025-09-07T06:05:04.933Z","repository":{"id":263302014,"uuid":"889953986","full_name":"StevenCyb/golang-functional-options","owner":"StevenCyb","description":"Flexible configuration with functional options pattern.","archived":false,"fork":false,"pushed_at":"2024-11-17T17:03:34.000Z","size":6,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-26T07:07:43.821Z","etag":null,"topics":["configuration","go","golang","initialization","instantiation","options","pattern"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/StevenCyb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-17T16:55:58.000Z","updated_at":"2024-11-17T17:04:35.000Z","dependencies_parsed_at":"2024-11-17T18:18:55.466Z","dependency_job_id":"6a316d30-796c-4063-a09d-35021bf0b06d","html_url":"https://github.com/StevenCyb/golang-functional-options","commit_stats":null,"previous_names":["stevencyb/golang-functional-options"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/StevenCyb/golang-functional-options","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenCyb%2Fgolang-functional-options","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenCyb%2Fgolang-functional-options/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenCyb%2Fgolang-functional-options/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenCyb%2Fgolang-functional-options/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/StevenCyb","download_url":"https://codeload.github.com/StevenCyb/golang-functional-options/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenCyb%2Fgolang-functional-options/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274001282,"owners_count":25205225,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-07T02:00:09.463Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["configuration","go","golang","initialization","instantiation","options","pattern"],"created_at":"2024-11-22T21:15:19.786Z","updated_at":"2025-09-07T06:05:04.897Z","avatar_url":"https://github.com/StevenCyb.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Golang Functional Options Pattern\n\nThe Functional Options Pattern in Go (Golang) is a design pattern that enables flexible and readable object or function configuration. By defining functions (options) that modify the attributes or behavior of a struct during its initialization, this pattern allows customization without the need for numerous constructors. It simplifies handling optional parameters, avoids bulky struct definitions, and keeps code clean and maintainable. This approach promotes a declarative and extensible configuration style.\n\n\n- [Golang Functional Options Pattern](#golang-functional-options-pattern)\n\t- [Traditional Constructor Method](#traditional-constructor-method)\n\t- [Multiple Constructors for Each Configuration Variant](#multiple-constructors-for-each-configuration-variant)\n\t- [Using a Custom Config Struct](#using-a-custom-config-struct)\n\t- [Setter Function Pattern](#setter-function-pattern)\n\t- [Functional Options Pattern](#functional-options-pattern)\n\n## Traditional Constructor Method\n\nA common way to initialize an object in Go is through a constructor function that explicitly defines required and optional parameters. Here's an example:\n\n```go\ntype Client struct {\n\tbaseURL    string\n\theader     map[string]string\n\tlogger     ILogger\n\tbaseClient *http.Client\n}\n\nfunc New(baseURL string, header map[string]string, logger ILogger) *Client {\n\treturn \u0026Client{\n\t\tbaseURL:    baseURL,\n\t\theader:     header,\n\t\tlogger:     logger,\n\t\tbaseClient: \u0026http.Client{},\n\t}\n}\n```\nUsage Example:\n```go\nclient := New(\"https://api.example.com\", map[string]string{\"Authorization\": \"Bearer token\"}, myLogger)\n```\n\nThis method provides clarity by explicitly listing parameters. However, as the number of optional parameters grows, constructors can become cumbersome. Managing defaults and introducing new options may require additional constructors, leading to verbosity and reduced flexibility.\n\nThis approach works well for simple configurations with a small number of parameters. However, it becomes less practical for complex setups or frequent changes, where maintaining multiple constructors can make the codebase harder to manage.\n\n## Multiple Constructors for Each Configuration Variant\n\nTo handle varying configurations, developers often create separate constructors for each combination of parameters. Here's an example:\n\n```go\ntype Client struct {\n\tbaseURL    string\n\theader     map[string]string\n\tlogger     ILogger\n\tbaseClient *http.Client\n}\n\n// Constructor with only baseURL\nfunc New(baseURL string) *Client {\n\treturn \u0026Client{\n\t\tbaseURL:    baseURL,\n\t\theader:     map[string]string{},\n\t\tbaseClient: \u0026http.Client{},\n\t}\n}\n\n// Constructor with baseURL and headers\nfunc NewWithBaseURLAndHeaders(baseURL string, header map[string]string) *Client {\n\treturn \u0026Client{\n\t\tbaseURL:    baseURL,\n\t\theader:     header,\n\t\tbaseClient: \u0026http.Client{},\n\t}\n}\n\n// Constructor with baseURL, headers, and logger\nfunc NewWithBaseURLHeadersAndLogger(baseURL string, header map[string]string, logger ILogger) *Client {\n\treturn \u0026Client{\n\t\tbaseURL:    baseURL,\n\t\theader:     header,\n\t\tlogger:     logger,\n\t\tbaseClient: \u0026http.Client{},\n\t}\n}\n```\nUsage Example:\n```go\n// With base url only.\nclient := New(\"https://api.example.com\")\n\n// With base url and header.\nclient := NewWithBaseURLAndHeaders(\"https://api.example.com\", map[string]string{\"Authorization\": \"Bearer token\"})\n\n// With all attributes.\nclient := NewWithBaseURLHeadersAndLogger(\"https://api.example.com\", map[string]string{\"Authorization\": \"Bearer token\"}, myLogger)\n```\n\nThis method accommodates various configurations but can lead to constructor bloat as the number of variants increases.\n\nThis approach is suitable for predictable configuration sets but quickly becomes unwieldy for more dynamic or extensible configurations. It may lead to a cluttered codebase and reduced maintainability.\n\n## Using a Custom Config Struct\n\nA `Config` struct centralizes all configuration options, which can then be passed to a single constructor. Here's an example:\n\n```go\ntype Config struct {\n\tBaseURL string\n\tHeader  map[string]string\n\tLogger  ILogger\n}\n\ntype Client struct {\n\tbaseURL    string\n\theader     map[string]string\n\tlogger     ILogger\n\tbaseClient *http.Client\n}\n\nfunc NewWithConfig(config *Config) *Client {\n\treturn \u0026Client{\n\t\tbaseURL:    config.BaseURL,\n\t\theader:     config.Header,\n\t\tlogger:     config.Logger,\n\t\tbaseClient: \u0026http.Client{},\n\t}\n}\n```\nUsage Example:\n```go\nconfig := \u0026Config{\n\tBaseURL: \"https://api.example.com\",\n\tHeader:  map[string]string{\"Authorization\": \"Bearer token\"},\n\tLogger:  myLogger,\n}\nclient := NewWithConfig(config)\n```\n\nUsing a Config struct simplifies the constructor and improves maintainability.\n\nThis approach is effective for centralizing configuration. However, it may lack the expressiveness and flexibility of other patterns, particularly when adding dynamic or conditional configurations.\n\n## Setter Function Pattern\n\nThe Setter Function Pattern initializes an object using a basic constructor, followed by setter methods for additional configuration.\n\n```go\ntype Client struct {\n\tbaseURL    string\n\theader     map[string]string\n\tlogger     ILogger\n\tbaseClient *http.Client\n}\n\n// Basic constructor\nfunc New(baseURL string) *Client {\n\treturn \u0026Client{\n\t\tbaseURL:    baseURL,\n\t\theader:     map[string]string{},\n\t\tbaseClient: \u0026http.Client{},\n\t}\n}\n\n// Setter for headers\nfunc (c *Client) SetHeader(header map[string]string) *Client {\n\tc.header = header\n\treturn c\n}\n\n// Setter for logger\nfunc (c *Client) SetLogger(logger ILogger) *Client {\n\tc.logger = logger\n\treturn c\n}\n```\nUsage Example:\n```go\n// On instantiation\nclient := New(\"https://api.example.com\").\n\tSetHeader(map[string]string{\"Authorization\": \"Bearer token\"}).\n\tSetLogger(myLogger)\n// Split\nclient := New(\"https://api.example.com\")\nclient.SetHeader(map[string]string{\"Authorization\": \"Bearer token\"})\nclient.SetLogger(myLogger)\n```\n\nThis method allows incremental configuration by chaining setter calls.\n\nSetter methods are useful for incremental configurations and a fluent API style. However, they may complicate validation and initialization logic if setters are misused or called in the wrong order.\n\n## Functional Options Pattern\nThe Functional Options Pattern provides a clean and flexible way to configure objects by passing functions (options) to a single constructor.\n\n```go\ntype ILogger interface{}\n\ntype Client struct {\n\tbaseURL    string\n\theader     map[string]string\n\tlogger     ILogger\n\tbaseClient *http.Client\n}\n\n// Option type\ntype Option func(*Client)\n\n// Basic constructor with functional options\nfunc New(baseURL string, opts ...Option) *Client {\n\tclient := \u0026Client{\n\t\tbaseURL:    baseURL,\n\t\theader:     map[string]string{},\n\t\tbaseClient: \u0026http.Client{},\n\t}\n\n\t// Apply each option\n\tfor _, opt := range opts {\n\t\topt(client)\n\t}\n\treturn client\n}\n\n// Option to set headers\nfunc WithHeader(header map[string]string) Option {\n\treturn func(c *Client) {\n\t\tc.header = header\n\t}\n}\n\n// Option to set logger\nfunc WithLogger(logger ILogger) Option {\n\treturn func(c *Client) {\n\t\tc.logger = logger\n\t}\n}\n```\nUsage Example:\n```go\nclient := New(\"https://api.example.com\",\n\tWithHeader(map[string]string{\"Authorization\": \"Bearer token\"}),\n\tWithLogger(myLogger),\n)\n```\n\nThe pattern enables developers to configure an object in a highly customizable and expressive way.\n\nThis approach is ideal for complex configurations with many optional parameters. It is extensible, avoids constructor bloat, and supports a clean API. However, it can add complexity to debugging and understanding code due to the indirection introduced by options.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevencyb%2Fgolang-functional-options","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevencyb%2Fgolang-functional-options","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevencyb%2Fgolang-functional-options/lists"}