应使用函数式选项模式(type Option func(*T))替代结构体或map传参,因其类型安全、可组合、IDE友好;WithXxx函数需内置校验并返回Option闭包;NewClient需支持变参、先设默认值再应用选项,并在Option内处理错误与内存泄漏。

为什么不能直接传结构体或 map
传结构体字面量容易漏字段、加新字段就得改所有调用点,比如 NewClient("api.example.com", 0, 0, false, nil) —— 第二个 0 是超时还是重试次数?根本分不清。用 map[string]interface{} 更糟:键名拼错("timeot")编译器不拦,运行时才 panic,IDE 补全失效,类型安全全丢。
Option 类型必须是 func(*T),不是 interface 或 struct
常见错误是定义成 type Option interface{ Apply(*Client) } 或 type Option struct{ Timeout int }。前者让 IDE 跳转失效、类型推导丢失;后者本质是数据容器,没法校验、无法组合、还容易漏传字段。正确写法只有一行:type Option func(*Client)。所有 WithXxx 函数必须返回匹配该签名的闭包,例如:func WithTimeout(d time.Duration) Option { return func(c *Client) { c.timeout = d } }。
构造函数里怎么安全 apply 多个 Option
签名必须支持变参:func NewClient(opts ...Option) *Client。内部执行分三步:
• 先设默认值:c := &Client{timeout: 30 * time.Second, retries: 3}
• 再遍历应用:for _, opt := range opts { opt(c) }
• 如果某个 option 可能失败(比如校验证书路径),就让它返回 error,构造函数里必须检查:if err := opt(c); err != nil { return nil, err }。
注意:默认值必须在 apply 前设置,否则用户无法“清空”某个字段;选项执行顺序影响结果,后设的覆盖先设的,依赖顺序的逻辑建议合并到一个 Option 里。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
校验逻辑该放哪、不该放哪
校验必须放在 WithXxx 函数内部,而不是构造函数里。比如 WithTimeout 应该检查 d > 0,而不是等所有选项都 apply 完再统一判断——否则用户传入 WithTimeout(-1) 就已经污染了配置。不要在闭包里捕获大对象(如整个 *http.Client),防止内存泄漏。如果需要显式禁用某行为,提供 WithoutLogging 这类选项,而不是依赖传零值或顺序。
立即学习“go语言免费学习笔记(深入)”;

















