go中无传统简单工厂,因无类和构造函数重载,所谓工厂仅为返回接口的普通函数,如newprinter,核心是解耦创建逻辑与调用方,需确保接口提前声明、方法签名匹配、返回接口而非具体类型。

为什么 Go 里没有传统意义上的“简单工厂”
Go 没有类、没有构造函数重载、也没有 new 关键字,所谓“工厂”只是普通函数返回接口类型。它不是语言特性,而是组织创建逻辑的习惯写法。NewPrinter 或 NewPaymentService 这类函数才是实际的“工厂”,本质是封装了初始化细节和依赖组装。
常见错误现象:cannot use &MyStruct{} as MyInterface (missing method) —— 忘记实现接口全部方法,却直接在工厂里返回指针;或者误以为只要类型名匹配就能自动适配。
- 接口定义必须在工厂函数签名前声明,否则无法返回接口类型
- 工厂函数应返回接口而非具体结构体,否则失去多态意义
- 若结构体字段未导出(小写开头),外部包无法用字面量初始化,必须靠工厂函数
怎么写一个真正解耦的简单工厂函数
核心是把“创建谁”和“怎么创建”分开:调用方只依赖接口,不感知具体类型;工厂内部决定实例化哪个结构体,并完成必要字段赋值或依赖注入。
使用场景:配置驱动后端服务类型(如日志输出到 file / kafka / stdout)、支付网关切换(alipay / wechat / mock)。
立即学习“go语言免费学习笔记(深入)”;
- 工厂函数名推荐以
New开头,返回接口类型,例如func NewLogger(cfg Config) Logger - 避免在工厂里做 heavy 初始化(如建连接、读大文件),除非明确需要立即生效
- 如果参数多且易变,考虑传入结构体选项(
Option函数式配置),而不是堆砌参数列表
简短示例:
type Writer interface {
Write([]byte) error
}
<p>type FileWriter struct{ path string }
func (f <em>FileWriter) Write(p []byte) error { /</em> ... */ }</p><p>func NewWriter(kind string, cfg map[string]string) Writer {
switch kind {
case "file":
return &FileWriter{path: cfg["path"]}
case "stdout":
return os.Stdout // *os.File 实现了 Writer
default:
panic("unknown writer kind")
}
}接口实现容易漏掉的两个关键点
Go 的接口实现是隐式的,编译器只检查方法签名是否完全匹配,但有两个地方极易出错,导致运行时 panic 或行为异常。
- 方法接收者类型不一致:接口要求
func (t T) Method(),你写了func (t *T) Method(),反之亦然 —— 这会导致类型无法赋值给接口 - 大小写问题:
Write和write是两个不同方法;接口中定义的是Write,但结构体实现了write,编译不报错(因为是合法方法),但不满足接口
验证方式很简单:在工厂函数返回前加一行 _ = Writer(myInstance),让编译器强制检查是否实现。
什么时候不该用简单工厂
当创建逻辑开始出现条件嵌套加深、分支超过 3–4 种、或需要缓存/单例/生命周期管理时,“简单工厂”就不再是最佳选择。
- 如果工厂函数里开始出现
if err != nil { return nil, err }多次,说明它已承担了太多职责(错误处理 + 创建 + 配置校验) - 若多个地方要共享同一个实例(比如全局 logger),应该用 sync.Once + 包级变量,而不是每次调用工厂都新建
- 需要根据上下文动态选型(如按用户 ID 哈希路由到不同 DB 实例),更适合用策略模式 + 注册表,而非硬编码 switch
简单工厂的价值在于“一眼看清创建路径”,一旦这个路径变得模糊或承载了不该有的责任,就该停下来重构了。










