问题背景: 在设计API 时,如何处理可选配置?
好处:解决兼容性,但问题是0值,和可读性差
如何解决0值? ——使用指针,将nil和类型0值做区分
但是入参包含结构体,可读性差无法解决
生成器模式(Builder Pattern)是一种创建型设计模式,用于构建复杂对象。该模式将对象的构造过程与其表示分离,使同样的构建过程可以创建不同的表示。
从您提供的代码中,我们可以看到一个经典的 Go 语言生成器模式实现:
// 构建器类型
type ConfigBuilder struct {
port *int
}
// 配置方法,支持链式调用
func (b *ConfigBuilder) Port(port int) *ConfigBuilder {
b.port = &port
return b
}
// 最终构建对象
func (b *ConfigBuilder) Build() (Config, error) {
// 复杂的构建逻辑...
return cfg, nil
}
分步构造
builder := ConfigBuilder{}
builder.Port(8080)
// 可以添加更多配置...
cfg, err := builder.Build()
允许分步设置对象属性,构造过程清晰可控。
参数验证
if *b.port < 0 {
return Config{}, errors.New("port should be positive")
}
可以在构建期间进行参数验证,而不是在使用时才发现问题。
默认值处理
if b.port == nil {
cfg.Port = defaultHTTPPort
}
优雅地处理未指定参数的默认值。
复杂构建逻辑封装
if *b.port == 0 {
cfg.Port = randomPort()
}
封装内部的构建复杂性,对使用者透明。
流畅的 API
// 理论上可以支持链式调用
builder.Port(8080).Timeout(30).TLS(cert, key)
可以设计成支持链式调用的 API,提高可读性。
代码量增加
构造过程复杂化
// 比直接使用构造函数需要更多步骤
builder := ConfigBuilder{}
builder.Port(8080)
cfg, err := builder.Build()
server, err := NewServer("localhost", cfg)
相比直接使用构造函数,需要更多步骤。
不适合简单对象
可变状态
构造复杂对象
参数验证需求
if *b.port < 0 {
return Config{}, errors.New("port should be positive")
}
需要在构造过程中进行多重验证。
多种表示形式
参数可选性高
相比于代码中提到的其他模式:
现实世界中,生成器模式适用于配置数据库连接、HTTP客户端、日志记录器等复杂对象:
// 典型的 SQL 构建器
query := sqlBuilder.
Select("name, age").
From("users").
Where("age > ?", 18).
OrderBy("name ASC").
Limit(10).
Build()
在需要灵活构建复杂对象、有多个可选参数和复杂验证逻辑的场景中,生成器模式是一个很好的选择。
功能选项模式是Go语言中一种优雅的API设计模式,用于解决具有多个可选配置参数的函数或构造器的问题。它通过函数闭包实现灵活、可扩展且易用的API设计。
功能选项模式包含三个主要组件:
选项类型定义:一个函数类型,接收内部配置结构并修改它
type Option func(options *options) error
选项生成器:返回符合选项类型的函数的工厂方法
func WithPort(port int) Option {
return func(options *options) error {
if port < 0 {
return errors.New("port should be positive")
}
options.port = &port
return nil
}
}
主函数:接收变长的选项参数并应用它们
func NewServer(addr string, opts ...Option) (*http.Server, error) {
var options options
for _, opt := range opts {
err := opt(&options)
if err != nil {
return nil, err
}
}
// 使用options创建并返回对象...
}
您提供的代码展示了三种不同的API设计模式:
func NewServer(addr string, cfg Config) {
}
func main() {
NewServer("localhost", Config{})
}
缺点:必须总是提供完整结构体,即使只需设置一个参数
builder := ConfigBuilder{}
builder.Port(8080)
cfg, err := builder.Build()
server, err := NewServer("localhost", cfg)
缺点:需要额外的构建器类型,多个步骤创建对象
_, _ = NewServer("localhost", WithPort(8080))
优点:简洁、流畅的API,单步创建对象
WithPort
)// 创建使用默认值的服务器
server, _ := NewServer("localhost")
// 创建有自定义端口的服务器
server, _ := NewServer("localhost", WithPort(9000))
// 组合多个选项
server, _ := NewServer("localhost",
WithPort(9000),
WithTimeout(30),
WithTLS(cert, key))
功能选项模式是Go语言中构建灵活、易用且可扩展API的强大工具,特别适合具有多个可选参数的场景。