

新闻资讯
行业动态建造者模式适用于多可选参数、需校验与分步配置的场景,如HTTPClient;工厂模式仅适合参数固定、变体极少的情况;二者组合时应通过私有字段、链式方法和Build()校验实现可控扩展。
工厂模式管“造什么”,建造者模式管“怎么造”——选错就容易写一堆冗余代码或失去配置灵活性。
NewHTTPClientBuilder() 而不是 NewHTTPClient()?当对象初始化需要多个可选参数、依赖注入、校验逻辑或分步设置时,直接构造函数会迅速失控。比如 HTTPClient 至少涉及 Timeout、Retry、Middlewares、Transport 等 5+ 配置项,且不同业务场景组合差异大。
NewHTTPClient())只适合参数固定、变体极少的场景(例如只有一种默认客户端)NewHTTPClientBuilder().WithTimeout(30 * time.Second).WithRetry(3).Build())才能支持链式、可读、可复用的配置流NewClientBuilder("prod") → 返回带默认超时/重试/日志中间件的 *HTTPClientBuilder
工厂模式本身不处理“配置过程”,它只负责“分发”。一旦你试图在工厂里塞进所有配置逻辑(比如 NewHTTPClient(timeout, retry, middleware...)),就会立刻掉进两个坑:
MaxIdleConns 就得改函数签名,所有调用点都要动map[string]interface{} 或结构体传参,编译期无法检查字段合法性而建造者把校验和默认值封装在 Build() 里,比如未设 Timeout 时返回 error,或自动设为 30s,这才是可控的扩展方式。
核心就三样:统一接口、具体建造者、工厂函数。不需要 Director,也不需要抽象 Builder 接口——Go 里靠值语义和函数链式调用更轻量。
type LoggerBuilder interface {
WithLevel(level string) LoggerBuilder
WithFormat(format string) LoggerBuilder
Build() (Logger, error)
}
type fileLoggerBuilder struct {
level string
format string
path string
}
func (b *fileLoggerBuilder) WithLevel(level string) LoggerBuilder {
b.level = level
return b
}
// ... 其他方法
func NewLoggerBuilder(kind string) LoggerBuilder {
switch kind {
case "file":
return &fileLoggerBuilder{level: "info"} // 默认值在此
case "console":
return &consoleLoggerBuilder{level: "debug"}
default:
panic("unknown logger kind")
}
}
工厂函数名要体现意图,比如 NewLoggerBuilder() 比 NewBuilder() 更明确Build() 是唯一出口,也是校验入口*fileLoggerBuilder),除非你确认并发安全;值接收器 + 每次返回新实例更稳妥最容易被忽略的是:建造者不是为“炫技”而存在,而是为“防止配置遗漏”和“降低调用方认知负担”。如果一个对象创建后 90% 的调用都用同一组参数,那它大概率不该用建造者;但如果每次初始化都要查文档确认字段含义,那就该立刻重构出建造者——哪怕只封装三个字段。