Patrón de Opciones Funcionales
El patrón de opciones funcionales configura tipos complejos a través de una lista variádica de funciones Option pasadas a un constructor.
Busca en todas las páginas de la documentación
El patrón de opciones funcionales configura tipos complejos a través de una lista variádica de funciones Option pasadas a un constructor.
Reemplaza las sobrecargas telescópicas de New y las estructuras de constructor mutables con configuraciones componibles e independientes del orden.
Las opciones funcionales modelan cada perilla de configuración como func(*T) aplicado dentro de New.
Los llamadores pasan solo las configuraciones que les importan; el constructor aplica primero los valores predeterminados y luego ejecuta cada opción.
El patrón es idiomático para bibliotecas y clientes de infraestructura donde existen muchas configuraciones ortogonales y la compatibilidad binaria es importante.
Cambia una pequeña cantidad de código repetitivo por APIs que se mantienen legibles a medida que crecen.
Tarjeta de receta de referencia rápida: lista para copiar y pegar.
type Option func(*Client) error
func WithTimeout(d time.Duration) Option {
return func(c *Client) error {
if d <= 0 { return errors.New("el tiempo de espera debe ser positivo") }
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
}Cuándo usar esto:
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: se requiere URL base") }
c.base = url
return nil
}
}
func WithAPIKey(key string) Option {
return func(c *Client) error {
if key == "" { return errors.New("weather: se requiere clave API") }
c.apiKey = key
return nil
}
}
func WithHTTPClient(hc *http.Client) Option {
return func(c *Client) error {
if hc == nil { return errors.New("weather: se requiere cliente http") }
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: se requiere clave API")
}
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: estado %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"))
}Lo que esto demuestra:
base, tiempo de espera de http.Client) residen dentro de NewWith* valida su propia entradaNew aplica reglas entre campos (se requiere clave API)Option es un tipo de función cerrado sobre valores de configuraciónNew asigna el objetivo, establece valores predeterminados y luego aplica opts en ordenWith*; mantén Option sin exportar cuando sea posible| Escenario | Comportamiento | Recomendación |
|---|---|---|
| Configuraciones independientes | El orden no importa | Documentar como independiente del orden |
| Gana el último | La opción posterior sobrescribe la anterior | Documentar explícitamente o rechazar duplicados |
| Modos TLS mutuamente exclusivos | Combinación inválida | Devolver error de la segunda opción o de New |
| Variante | Firma | Usar Cuando |
|---|---|---|
| Opciones silenciosas | func(*T) | Configuraciones simples, validación sin pánico en New |
| Opciones de validación | func(*T) error | Fallos de validación por opción |
| Estructura de opciones funcionales | Config exportada + New(Config) | Todos los campos se proporcionan siempre juntos |
// Prefiere el tipo Option no exportado en el ámbito del paquete.
type option func(*Server)
// Exporta constructores para facilitar el descubrimiento.
func WithAddr(addr string) option { /* ... */ }( *T, error) de New cuando la validación pueda fallarWithTimeout, no SetTimeout (mutación implícita)New y las invariantes se rompen. Solución: mantén los campos de configuración sin exportar.New(a, b) es más claro que New(WithA(a), WithB(b)) para tipos triviales. Solución: reserva las opciones para 3 o más configuraciones ortogonales.WithHTTPClient(nil) provoca un pánico en el uso. Solución: valida que no sea nulo en la función de opción.WithTTL para que signifique un campo diferente rompe silenciosamente a los llamadores. Solución: agrega nuevas funciones With*; descontinúa los nombres antiguos en las versiones.| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Parámetro de estructura de configuración | Todos los campos se establecen juntos, pocos ajustes opcionales | Muchas configuraciones ortogonales opcionales |
| Estructura de constructor con métodos | API fluida para DSLs internas | API pública de biblioteca (más difícil de evolucionar) |
| Opciones funcionales | Constructores de bibliotecas con valores predeterminados | Solo uno o dos parámetros requeridos |
| Solo variables de entorno | Herramientas CLI, aplicaciones doce factor | Bibliotecas consumidas por otros módulos |
El patrón aparece en las primeras publicaciones de blog de Go y se usa ampliamente en google.golang.org/grpc, OpenTelemetry y bibliotecas adyacentes a la biblioteca estándar.
Coincide con la preferencia de Go por las funciones sobre la herencia.
Generalmente no: exporta las funciones With* y mantén type option func(*T) sin exportar para que los llamadores no puedan crear opciones que eludan la validación.
Diseña opciones independientes para que conmuten.
Cuando el orden importa, documéntalo o detecta conflictos y devuelve errores.
Prueba en tablas New con subconjuntos de opciones: solo valores predeterminados, anulación única, opción inválida, opciones conflictivas.
Mantén las opciones baratas; delega el trabajo pesado (carga de certificados TLS) a New después de que se apliquen todas las opciones, o inicializa perezosamente en el primer uso.
Las estructuras de configuración agrupan campos requeridos; las opciones brillan cuando la mayoría de los campos tienen valores predeterminados y los llamadores anulan unos pocos.
No hay un límite fijo: gRPC tiene docenas.
Agrupa configuraciones relacionadas detrás de ayudantes de opciones anidados si los nombres abarrotan godoc.
Go 1.18+ puede parametrizar genéricamente los ayudantes, pero el patrón clásico Option func(*T) sigue siendo el predeterminado de la comunidad para los constructores.
Devuelve una interfaz o un tipo opaco desde New cuando la estructura concreta deba permanecer oculta.
Devuelve error de New o de las opciones para código de biblioteca.
Reserva panic solo para errores de programador en paquetes internos.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (predeterminado de GC Green Tea, go fix modernizers - verifica el parche en la compilación), chi (última versión - verifica en la compilación), gin (última versión - verifica en la compilación), echo (última versión - verifica en la compilación), google.golang.org/grpc (última versión - verifica en la compilación), sigs.k8s.io/controller-runtime (última versión - verifica en la compilación), kubebuilder (última versión - verifica en la compilación), tinygo (última versión - verifica objetivos de placa en la compilación), wazero (última versión - verifica en la compilación) y golangci-lint (última versión - verifica en la compilación).
Revisado por Chris St. John·Última actualización: 16 jul 2026