如何使用Golang实现建造者模式对象组装_使用Builder Pattern组合对象

Go中建造者模式通过结构体Builder或函数式选项实现对象组装,分离构造逻辑、避免参数爆炸,支持链式调用与默认值校验。

建造者模式(Builder Pattern)在 Go 中不依赖类和继承,而是通过结构体、函数式选项和链式调用实现清晰、可扩展的对象组装。核心思路是:把对象的构造逻辑从初始化中分离出来,让调用方按需选择配置项,避免参数爆炸和不可变对象的繁琐构造。

定义目标结构体与私有字段

先设计最终要构建的对象,字段设为小写(私有),防止外部直接赋值:

例如构建一个 HTTP 客户端配置:

type HTTPClient struct {
    baseURL     string
    timeout     time.Duration
    retries     int
    userAgent   string
    middleware  []func(http.RoundTripper) http.RoundTripper
}

所有字段都不导出,确保只能通过 Builder 控制创建过程。

创建 Builder 结构体并提供设置方法

Builder 是一个普通结构体,持有和目标对象相同的字段(或中间状态),每个设置方法返回 *Builder 自身,支持链式调用:

type HTTPClientBuilder struct {
    cfg HTTPClient
}

func NewHTTPClientBuilder() HTTPClientBuilder { return &HTTPClientBuilder{ cfg: HTTPClient{ timeout: 30 time.Second, retries: 3, }, } }

func (b HTTPClientBuilder) WithBaseURL(url string) HTTPClientBuilder { b.cfg.baseURL = url return b }

func (b HTTPClientBuilder) WithTimeout(d time.Duration) HTTPClientBuilder { b.cfg.timeout = d return b }

func (b HTTPClientBuilder) WithRetries(n int) HTTPClientBuilder { b.cfg.retries = n return b }

func (b HTTPClientBuilder) WithUserAgent(ua string) HTTPClientBuilder { b.cfg.userAgent = ua return b }

func (b HTTPClientBuilder) WithMiddleware(ms ...func(http.RoundTripper) http.RoundTripper) HTTPClientBuilder { b.cfg.middleware = append(b.cfg.middleware, ms...) return b }

提供 Build 方法完成最终构造

Build 方法返回不可变的目标对象(通常做一次深拷贝或只读封装)。Go 中常直接返回结构体值(值语义天然不可变):

func (b *HTTPClientBuilder) Build() HTTPClient {
    // 可在此校验必要字段,如:
    if b.cfg.baseURL == "" {
        panic("baseURL is required")
    }
    return b.cfg // 返回副本,调用方无法修改原始 builder 状态
}

使用示例:

client := NewHTTPClientBuilder().
    WithBaseURL("https://api.example.com").
    WithTimeout(10 * time.Second).
    WithRetries(5).
    WithUserAgent("MyApp/1.0").
    Build()

进阶:用函数式选项(Functional Options)替代 Builder 结构体

更 Go 风格的做法是用函数类型封装配置逻辑,轻量且组合灵活:

type Option func(*HTTPClient)

func WithBaseURL(url string) Option { return func(c *HTTPClient) { c.baseURL = url } }

func WithTimeout(d time.Duration) Option { return func(c *HTTPClient) { c.timeout = d } }

func NewHTTPClient(opts ...Option) HTTPClient { c := HTTPClient{ timeout: 30 * time.Second, retries: 3, } for _, opt := range opts { opt(&c) } if c.baseURL == "" { panic("baseURL is required") } return c }

调用更简洁:

client := NewHTTPClient(
    WithBaseURL("https://api.example.com"),
    WithTimeout(10 * time.Second),
    WithRetries(5),
)

基本上就这些。Builder 模式在 Go 里不是照搬 Java 写法,而是借其思想——解耦构造、提升可读性、支持默认值与校验。用结构体 Builder 适合复杂流程;用 Functional Options 更符合 Go 的简洁哲学。选哪种,看你的配置维度和团队习惯。