go中抽象工厂用接口+工厂函数实现,不依赖继承;定义产品族接口,工厂函数返回接口类型,确保解耦与可测试性。

Go 里没有抽象类,怎么写抽象工厂
抽象工厂在 Go 里不是靠继承模拟出来的,而是用接口 + 工厂函数组合实现的。核心是把“创建什么”和“怎么创建”彻底分开,避免 NewProductA 这类硬编码散落在业务逻辑里。
常见错误是试图用空结构体嵌套接口、或强行造一个 AbstractFactory 类型——Go 不支持抽象类,这么干只会让代码更绕、更难测试。
- 用接口定义产品族行为(比如
Button、Checkbox),不定义构造逻辑 - 每个具体工厂实现为普通函数或结构体方法,返回符合接口的实例
- 工厂本身不继承任何东西,只依赖产品接口;产品实现也不引用工厂,解耦干净
什么时候该用抽象工厂,而不是简单工厂或依赖注入
当你需要同时创建一组有内在关联的对象(比如 WindowsButton 和 WindowsCheckbox 必须配对使用),且不同环境要切换整套实现(macOS / Windows / Web)时,抽象工厂才真正有用。
如果只是根据字符串参数选一个类型(如 "mysql" 或 "postgres"),用简单工厂或配置驱动的构造函数更轻量;盲目套抽象工厂反而增加调用链和测试负担。
立即学习“go语言免费学习笔记(深入)”;
- 适用场景:UI 组件库多主题支持、数据库驱动适配层、跨平台 SDK 封装
- 不适用场景:单个对象创建、运行时动态加载未知类型、配置项极少的二选一初始化
- 注意:Go 的依赖注入工具(如
dig、wire)能替代部分抽象工厂职责,但无法表达“产品族约束”,这点不能混用
func NewWindowsFactory() 返回值类型怎么设计
工厂函数返回值必须是接口,不是具体类型。否则调用方又得做类型断言或 import 具体实现包,破坏封装。
典型错误是返回 *windowsFactory 或 struct{},导致下游强依赖实现细节;或者把工厂接口定义得太宽(比如塞进 Destroy()、Reset()),偏离“创建”本职。
- 推荐返回一个干净的工厂接口,例如:
type GUIFactory interface { CreateButton() Button; CreateCheckbox() Checkbox } - 具体工厂可定义为无字段结构体(
type windowsFactory struct{}),方法直接返回对应实现 - 避免在工厂接口里暴露产品内部状态方法(如
Button.Color()),那是产品接口的事
测试抽象工厂时最容易漏掉的点
测试重点不在工厂“能不能 new 出来”,而在“返回的产品是否真能协同工作”。比如 CreateButton() 和 CreateCheckbox() 返回的实例,是否共享同一套样式上下文、事件总线或资源池。
很多人只测单个方法返回值是否非 nil,结果上线后发现按钮点击没响应——因为 MacCheckbox 用的是 NSControl 机制,而 MacButton 没接入同一事件循环。
- 必须写集成测试:用某个工厂创建全套产品,再触发实际交互路径(如点击 → 触发回调 → 检查状态)
- 工厂函数本身应无副作用,不读环境变量、不打开文件——否则测试不稳定
- 如果产品实现依赖全局状态(如
log.SetOutput),工厂需提供 clean-up 钩子,否则测试间会污染
抽象工厂真正的复杂性不在写法,而在于产品族之间的隐式契约。这些契约不会出现在代码签名里,只能靠文档和测试守住。










