Go工厂模式核心是统一创建入口与策略可插拔,用接口+函数值+map实现,避免interface{}和if/else链;简单工厂注册构造函数而非实例,抽象工厂仅用于多组件环境协同;需注意注册时机、错误处理与并发安全。

Go 里工厂模式不是用来模拟 Java 多态的,而是为了把 new 操作收口、解耦创建逻辑、支持运行时注册——用接口 + 函数值 + map 就够了,别写 interface{} 和 if/else 链。
为什么 NewXXX() 函数不是好工厂
直接写 NewJSONProcessor() 或 NewAlipay() 看似简单,但一旦类型超过 3 个、创建逻辑要读配置或校验参数,就会在业务代码里重复散落。更麻烦的是:所有调用点都强依赖具体类型,改一个构造方式就得全局 grep 修改。
- 工厂的价值不在“多态”,而在“统一入口”和“策略可插拔”
- 返回具体类型(如
*JSONProcessor)会让调用方绕过接口契约,后续加新实现时无法透明替换 - 若构造过程含副作用(如初始化连接池),分散调用会导致资源重复创建或泄漏
简单工厂:用 map[string]func() Interface 注册构造函数
这是最常用、也最符合 Go 习惯的写法。核心是延迟实例化、只注册函数、不提前创建对象。
-
processors是map[string]func() Processor,不是map[string]Processor——后者会在注册时立即执行构造,无法支持带参数或需上下文的初始化 - 注册必须在
init()或显式初始化阶段完成;若依赖外部配置(如 DB 地址),应改用Register(name, ctor)显式调用 - 工厂函数本身应轻量,heavy 初始化(如打开文件、连数据库)应放在具体类型的
NewXXX()内部,而非工厂分发逻辑中 - 并发场景下,
map非安全,读多写少可用sync.RWMutex包裹;若仅启动期注册,用sync.Once初始化 map 即可
抽象工厂:按环境返回整套协同组件
只有当多个对象需要成组使用且组合关系随环境变化时,才需要抽象工厂。比如 dev/staging/prod 下 Logger+Cache+DB 的不同实现组合。
立即学习“go语言免费学习笔记(深入)”;
- 先定义抽象工厂接口,如
type StorageFactory interface { CreateLogger() Logger; CreateCache() Cache } - 每个环境实现该接口(如
type DevFactory struct{}),但注意:这些结构体本身不应持有状态,否则易引发资源泄漏 - 抽象工厂通常配合依赖注入容器使用;若手动管理,务必确保整套组件生命周期一致(例如缓存关闭时日志也应 flush)
- 别为了“设计模式完整性”硬套抽象工厂——90% 的场景,一个带选项的简单工厂 + 接口组合就足够了
容易被忽略的细节:注册时机与 panic 边界
工厂函数里常见的 panic("unknown type") 不是优雅做法。真实服务中,类型名来自配置或用户输入,应该返回 error 并由上层决定 fallback 或告警。
-
Register调用必须早于任何NewXXX()调用,否则 map 为空导致 nil 返回或 panic - 若支持热加载(如通过 HTTP 接口动态注册),需用读写锁保护 map,且注意旧构造函数可能还在被 goroutine 使用中
- 接口方法签名要克制:比如
Processor只要Process(data string) error,别塞进Init()或Close()——那是具体类型自己的事


















