Padrão de Opções Funcionais
O padrão de opções funcionais configura tipos complexos através de uma lista variádica de funções Option passadas a um construtor.
Busque em todas as páginas da documentação
O padrão de opções funcionais configura tipos complexos através de uma lista variádica de funções Option passadas a um construtor.
Ele substitui sobrecargas New telescópicas e structs de builder mutáveis por configurações compostas e independentes de ordem.
Opções funcionais modelam cada configuração como func(*T) aplicado dentro de New.
Os chamadores passam apenas as configurações que lhes interessam; o construtor aplica os padrões primeiro e, em seguida, executa cada opção.
O padrão é idiomático para bibliotecas e clientes de infraestrutura onde existem muitas configurações ortogonais e a compatibilidade binária é importante.
Ele troca uma pequena quantidade de boilerplate por APIs que permanecem legíveis à medida que crescem.
Cartão de receita de referência rápida - pronto para copiar e colar.
type Option func(*Client) error
func WithTimeout(d time.Duration) Option {
return func(c *Client) error {
if d <= 0 { return errors.New("timeout must be positive") }
c.timeout = d
return nil
}
}
func NewClient(opts ...Option) (*Client, error) {
c := &Client{timeout: 30 * time.Second}
for _, opt := range opts {
if err := opt(c); err != nil { return nil, err }
}
return c, nil
}Quando usar isso:
package weather
import (
"context"
"errors"
"fmt"
"net/http"
"time"
)
type Client struct {
base string
http *http.Client
apiKey string
}
type Option func(*Client) error
func WithBaseURL(url string) Option {
return func(c *Client) error {
if url == "" { return errors.New("weather: base URL required") }
c.base = url
return nil
}
}
func WithAPIKey(key string) Option {
return func(c *Client) error {
if key == "" { return errors.New("weather: API key required") }
c.apiKey = key
return nil
}
}
func WithHTTPClient(hc *http.Client) Option {
return func(c *Client) error {
if hc == nil { return errors.New("weather: http client required") }
c.http = hc
return nil
}
}
func New(opts ...Option) (*Client, error) {
c := &Client{
base: "https://api.weather.example",
http: &http.Client{Timeout: 10 * time.Second},
}
for _, opt := range opts {
if err := opt(c); err != nil {
return nil, err
}
}
if c.apiKey == "" {
return nil, errors.New("weather: API key required")
}
return c, nil
}
func (c *Client) Forecast(ctx context.Context, city string) (string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet,
fmt.Sprintf("%s/v1/forecast?city=%s", c.base, city), nil)
if err != nil { return "", err }
req.Header.Set("Authorization", "Bearer "+c.apiKey)
resp, err := c.http.Do(req)
if err != nil { return "", err }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("weather: status %d", resp.StatusCode)
}
return "sunny", nil
}
func main() {
client, err := New(
WithAPIKey("dev-key"),
WithHTTPClient(&http.Client{Timeout: 5 * time.Second}),
)
if err != nil { panic(err) }
fmt.Println(client.Forecast(context.Background(), "Austin"))
}O que isso demonstra:
base, timeout do http.Client) vivem dentro de NewWith* valida sua própria entradaNew aplica regras entre campos (chave de API necessária)Option é um tipo de função fechado sobre valores de configuraçãoNew aloca o alvo, define os padrões e, em seguida, aplica opts em ordemWith*; mantenha Option não exportado quando possível| Cenário | Comportamento | Recomendação |
|---|---|---|
| Configurações independentes | A ordem não importa | Documente como independente de ordem |
| O último vence | Opção posterior sobrescreve anterior | Documente explicitamente ou rejeite duplicatas |
| Modos TLS mutuamente exclusivos | Combinação inválida | Retorne erro da segunda opção ou de New |
| Variante | Assinatura | Use Quando |
|---|---|---|
| Opções silenciosas | func(*T) | Configurações simples, validação sem pânico em New |
| Opções de validação | func(*T) error | Falhas de validação por opção |
| Struct de opções funcionais | Config exportado + New(Config) | Todos os campos sempre fornecidos juntos |
// Prefira o tipo Option não exportado no escopo do pacote.
type option func(*Server)
// Exporte construtores para descoberta.
func WithAddr(addr string) option { /* ... */ }( *T, error) de New quando a validação puder falharWithTimeout, não SetTimeout (mutação implícita)New e os invariantes serão quebrados. Correção: mantenha os campos de configuração não exportados.New(a, b) é mais claro do que New(WithA(a), WithB(b)) para tipos triviais. Correção: reserve opções para 3+ configurações ortogonais.WithHTTPClient(nil) causa pânico no uso. Correção: valide não nulo na função de opção.WithTTL para significar um campo diferente quebra os chamadores silenciosamente. Correção: adicione novas funções With*; deprecie nomes antigos ao longo das versões.| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Parâmetro de struct de configuração | Todos os campos definidos juntos, poucos botões opcionais | Muitas configurações ortogonais opcionais |
| Struct de builder com métodos | API fluente para DSLs internas | API pública de biblioteca (mais difícil de evoluir) |
| Opções funcionais | Construtores de biblioteca com padrões | Apenas um ou dois parâmetros obrigatórios |
| Apenas variáveis de ambiente | Ferramentas CLI, aplicativos doze fatores | Bibliotecas consumidas por outros módulos |
O padrão aparece em posts antigos de blog Go e é amplamente utilizado em google.golang.org/grpc, OpenTelemetry e bibliotecas adjacentes à biblioteca padrão.
Ele corresponde à preferência do Go por funções em vez de herança.
Geralmente não - exporte funções With* e mantenha type option func(*T) não exportado para que os chamadores não possam forjar opções que ignorem a validação.
Projete opções independentes para comutar.
Quando a ordem importa, documente-a ou detecte conflitos e retorne erros.
Teste em tabela New com subconjuntos de opções: apenas padrões, substituição única, opção inválida, opções conflitantes.
Mantenha as opções baratas; adie o trabalho pesado (carregamento de certificado TLS) para New após todas as opções serem aplicadas, ou inicialize preguiçosamente no primeiro uso.
Structs de configuração agrupam campos obrigatórios; opções brilham quando a maioria dos campos tem padrões e os chamadores substituem alguns.
Não há limite fixo - grpc tem dezenas.
Agrupe configurações relacionadas sob helpers de opção aninhados se os nomes poluírem o godoc.
Go 1.18+ pode parametrizar helpers com tipos, mas o padrão clássico Option func(*T) permanece o padrão da comunidade para construtores.
Retorne uma interface ou tipo opaco de New quando a struct concreta precisar permanecer oculta.
Retorne error de New ou opções para código de biblioteca.
Reserve o pânico para erros de programador apenas em pacotes internos.
Versões da Pilha: Esta página foi escrita para Go 1.26.x (padrão Green Tea GC, go fix modernizers - verifique o patch na compilação), chi (última - verifique na compilação), gin (última - verifique na compilação), echo (última - verifique na compilação), google.golang.org/grpc (última - verifique na compilação), sigs.k8s.io/controller-runtime (última - verifique na compilação), kubebuilder (última - verifique na compilação), tinygo (última - verifique os alvos de placa na compilação), wazero (última - verifique na compilação) e golangci-lint (última - verifique na compilação).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026