
工厂函数必须返回接口,不能返回具体结构体
Go 没有类和继承,所谓“工厂方法模式”本质是用函数封装构造逻辑,并通过接口统一行为契约。如果 NewUserFactory 返回 *User 而不是 Userer 接口,下游代码就会直接依赖具体类型,新增 VIPUser 时必然要改调用方——这直接违反开闭原则。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 定义清晰的接口(如
Userer),包含所有下游需要调用的方法 - 工厂函数签名统一为
func() Userer或带参数的func(role string) Userer - 每个具体类型实现该接口,工厂内部用
switch或映射分发,但调用方完全感知不到
示例:
type Userer interface {
GetName() string
GetLevel() int
}
func NewUserFactory(role string) Userer {
switch role {
case "vip":
return &VIPUser{}
default:
return &User{}
}
}
避免在工厂里做 heavy 初始化或依赖注入
工厂函数应该轻量、无副作用、可预测。常见错误是把数据库连接、配置加载、日志初始化塞进 NewXXX 里,导致单元测试难写、并发不安全、启动变慢。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 工厂只负责 new + 基础字段赋值,复杂依赖走构造函数参数或选项函数(
Option) - 如果必须传依赖,用接口参数(如
logger Logger),而不是全局变量或单例 - 不要在工厂里调用
os.Getenv或flag.Parse()—— 这些该由 main 包提前处理好再传入
用选项函数(Functional Options)替代多参数工厂
当工厂需要支持灵活配置(比如创建 HTTP 客户端时控制超时、重试、中间件),硬编码一堆参数会让函数签名爆炸,也破坏可读性。直接上 ...Option 是 Go 社区共识做法。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 定义
type Option func(*Client),每个选项函数只改一个关注点 - 工厂接收
...Option,按顺序 apply,最后返回实例 - 避免选项之间隐式依赖(比如某个选项强制要求另一个选项已设置),否则容易踩空指针或逻辑错乱
示例:
type Client struct {
timeout time.Duration
retry int
}
type Option func(*Client)
func WithTimeout(d time.Duration) Option {
return func(c *Client) { c.timeout = d }
}
func NewClient(opts ...Option) *Client {
c := &Client{timeout: time.Second}
for _, opt := range opts {
opt(c)
}
return c
}
接口粒度要够小,但不能碎到没法组合
为了“符合开闭原则”,有人会把接口拆得极细:一个方法一个接口。结果是 type Reader interface{ Read() }、type Closer interface{ Close() }……调用方不得不同时传多个接口,反而增加耦合和认知负担。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 按使用场景聚合方法,比如
Storer同时含Save()和Delete(),因为业务层通常成对使用 - 避免让工厂返回多个小接口的组合体(如
func() (Reader, Writer)),这等于暴露实现细节 - 如果真需要组合,用嵌入接口(
type ReadWriter interface{ Reader; Writer }),而非返回元组
接口不是越多越“面向接口编程”,而是刚好能隔离变化点。
真正难的不是写个 NewXXX 函数,是判断哪个地方会变、谁会来扩展、以及接口边界划在哪——这些没想清楚,工厂只会变成新枷锁。










