go-functional-options by cxuu/golang-skills
npx skills add https://github.com/cxuu/golang-skills --skill go-functional-options来源 : Uber Go 风格指南
函数式选项是一种模式,您声明一个不透明的 Option 类型,该类型在内部结构体中记录信息。构造函数接受可变数量的这些选项,并应用它们来配置结果。
在以下情况使用函数式选项:
options 结构体 - 保存所有配置Option 接口 - 带有未导出的 apply 方法With* 构造函数 - 创建选项广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
type Option interface {
apply(*options)
}
未导出的 apply 方法确保只能使用来自此包的选项。
来源 : Uber Go 风格指南
package db
import "go.uber.org/zap"
// options 保存打开连接的所有配置。
type options struct {
cache bool
logger *zap.Logger
}
// Option 配置我们如何打开连接。
type Option interface {
apply(*options)
}
// cacheOption 为缓存设置实现 Option(简单类型别名)。
type cacheOption bool
func (c cacheOption) apply(opts *options) {
opts.cache = bool(c)
}
// WithCache 启用或禁用缓存。
func WithCache(c bool) Option {
return cacheOption(c)
}
// loggerOption 为日志记录器设置实现 Option(用于指针的结构体)。
type loggerOption struct {
Log *zap.Logger
}
func (l loggerOption) apply(opts *options) {
opts.logger = l.Log
}
// WithLogger 为连接设置日志记录器。
func WithLogger(log *zap.Logger) Option {
return loggerOption{Log: log}
}
// Open 创建一个连接。
func Open(addr string, opts ...Option) (*Connection, error) {
// 从默认值开始
options := options{
cache: defaultCache,
logger: zap.NewNop(),
}
// 应用所有提供的选项
for _, o := range opts {
o.apply(&options)
}
// 使用 options.cache 和 options.logger...
return &Connection{}, nil
}
来源 : Uber Go 风格指南
// 调用者必须始终提供所有参数,即使是默认值
db.Open(addr, db.DefaultCache, zap.NewNop())
db.Open(addr, db.DefaultCache, log)
db.Open(addr, false /* cache */, zap.NewNop())
db.Open(addr, false /* cache */, log)
// 仅在需要时提供选项
db.Open(addr)
db.Open(addr, db.WithLogger(log))
db.Open(addr, db.WithCache(false))
db.Open(
addr,
db.WithCache(false),
db.WithLogger(log),
)
| 方面 | 函数式选项 | 配置结构体 |
|---|---|---|
| 可扩展性 | 添加新的 With* 函数 | 添加新字段(可能破坏兼容性) |
| 默认值 | 内置在构造函数中 | 零值或单独的默认值 |
| 调用者体验 | 仅指定不同的部分 | 必须构造整个结构体 |
| 可测试性 | 选项可比较 | 结构体比较 |
| 复杂性 | 更多样板代码 | 设置更简单 |
在以下情况首选配置结构体:少于 3 个选项,选项很少更改,所有选项通常一起指定,或者仅用于内部 API。
来源 : Uber Go 风格指南
另一种实现使用闭包:
// 闭包方法(不推荐)
type Option func(*options)
func WithCache(c bool) Option {
return func(o *options) { o.cache = c }
}
首选接口方法,因为:
fmt.Stringer// 1. 带有默认值的未导出 options 结构体
type options struct {
field1 Type1
field2 Type2
}
// 2. 导出的 Option 接口,未导出的方法
type Option interface {
apply(*options)
}
// 3. 选项类型 + apply + With* 构造函数
type field1Option Type1
func (o field1Option) apply(opts *options) { opts.field1 = Type1(o) }
func WithField1(v Type1) Option { return field1Option(v) }
// 4. 构造函数在默认值之上应用选项
func New(required string, opts ...Option) (*Thing, error) {
o := options{field1: defaultField1, field2: defaultField2}
for _, opt := range opts {
opt.apply(&o)
}
// ...
}
options 结构体是未导出的Option 接口有未导出的 apply 方法With* 构造函数...Option 分开go-style-core - 核心 Go 风格原则go-naming - Go 命名约定go-defensive - 防御性编程模式每周安装次数
145
代码仓库
GitHub 星标数
35
首次出现
2026年1月27日
安全审计
安装于
github-copilot135
gemini-cli133
codex133
kimi-cli132
amp132
opencode132
Source : Uber Go Style Guide
Functional options is a pattern where you declare an opaque Option type that records information in an internal struct. The constructor accepts a variadic number of these options and applies them to configure the result.
Use functional options when:
options struct - holds all configurationOption interface - with unexported apply methodWith* constructors - create optionstype Option interface {
apply(*options)
}
The unexported apply method ensures only options from this package can be used.
Source : Uber Go Style Guide
package db
import "go.uber.org/zap"
// options holds all configuration for opening a connection.
type options struct {
cache bool
logger *zap.Logger
}
// Option configures how we open the connection.
type Option interface {
apply(*options)
}
// cacheOption implements Option for cache setting (simple type alias).
type cacheOption bool
func (c cacheOption) apply(opts *options) {
opts.cache = bool(c)
}
// WithCache enables or disables caching.
func WithCache(c bool) Option {
return cacheOption(c)
}
// loggerOption implements Option for logger setting (struct for pointers).
type loggerOption struct {
Log *zap.Logger
}
func (l loggerOption) apply(opts *options) {
opts.logger = l.Log
}
// WithLogger sets the logger for the connection.
func WithLogger(log *zap.Logger) Option {
return loggerOption{Log: log}
}
// Open creates a connection.
func Open(addr string, opts ...Option) (*Connection, error) {
// Start with defaults
options := options{
cache: defaultCache,
logger: zap.NewNop(),
}
// Apply all provided options
for _, o := range opts {
o.apply(&options)
}
// Use options.cache and options.logger...
return &Connection{}, nil
}
Source : Uber Go Style Guide
// Caller must always provide all parameters, even defaults
db.Open(addr, db.DefaultCache, zap.NewNop())
db.Open(addr, db.DefaultCache, log)
db.Open(addr, false /* cache */, zap.NewNop())
db.Open(addr, false /* cache */, log)
// Only provide options when needed
db.Open(addr)
db.Open(addr, db.WithLogger(log))
db.Open(addr, db.WithCache(false))
db.Open(
addr,
db.WithCache(false),
db.WithLogger(log),
)
| Aspect | Functional Options | Config Struct |
|---|---|---|
| Extensibility | Add new With* functions | Add new fields (may break) |
| Defaults | Built into constructor | Zero values or separate defaults |
| Caller experience | Only specify what differs | Must construct entire struct |
| Testability | Options are comparable | Struct comparison |
| Complexity | More boilerplate | Simpler setup |
Prefer Config Struct when : Fewer than 3 options, options rarely change, all options usually specified together, or internal APIs only.
Source : Uber Go Style Guide
An alternative implementation uses closures:
// Closure approach (not recommended)
type Option func(*options)
func WithCache(c bool) Option {
return func(o *options) { o.cache = c }
}
The interface approach is preferred because:
fmt.Stringer// 1. Unexported options struct with defaults
type options struct {
field1 Type1
field2 Type2
}
// 2. Exported Option interface, unexported method
type Option interface {
apply(*options)
}
// 3. Option type + apply + With* constructor
type field1Option Type1
func (o field1Option) apply(opts *options) { opts.field1 = Type1(o) }
func WithField1(v Type1) Option { return field1Option(v) }
// 4. Constructor applies options over defaults
func New(required string, opts ...Option) (*Thing, error) {
o := options{field1: defaultField1, field2: defaultField2}
for _, opt := range opts {
opt.apply(&o)
}
// ...
}
options struct is unexportedOption interface has unexported apply methodWith* constructor...Optiongo-style-core - Core Go style principlesgo-naming - Naming conventions for Gogo-defensive - Defensive programming patternsWeekly Installs
145
Repository
GitHub Stars
35
First Seen
Jan 27, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot135
gemini-cli133
codex133
kimi-cli132
amp132
opencode132
Perl安全编程指南:输入验证、注入防护与安全编码实践
786 周安装