Go工厂模式用func+interface+结构体组合实现创建逻辑封装与解耦,核心是返回接口而非具体struct以支持实现替换,配置应通过结构体或Option函数传递,避免上帝工厂,按领域拆分职责。

Go 语言没有类和继承,所谓“工厂模式”不是照搬 Java 那套抽象工厂 + 接口实现的模板,而是用 func、interface{} 和结构体组合来达成「封装创建逻辑、解耦使用者与具体类型」的目的。直接写 new(MyStruct) 不叫工厂;返回接口、隐藏构造细节、支持配置化创建,才算。
为什么 Go 的工厂函数通常返回 interface 而不是 struct
工厂的核心价值是让调用方不依赖具体类型。如果工厂返回 *MyService,调用方就和这个 struct 绑死了;而返回一个定义好的 Service 接口,后续就能轻松替换实现(比如从内存版换成 Redis 版),无需改调用代码。
- 接口定义应聚焦行为,例如:
type Service interface { Do() error Close() error } - 工厂函数签名应为:
func NewService(cfg Config) Service,而非func NewService() *ConcreteService - 若返回具体 struct,等于把实现细节暴露出去,违背了工厂的封装意图
带配置参数的工厂函数怎么设计
硬编码配置(如写死数据库地址)会让工厂不可复用。推荐用结构体传参,字段可导出供外部设置,也可加 Option 函数做链式配置(更灵活但稍重)。
- 基础方式:用配置结构体
type DBConfig struct { Addr string Timeout time.Duration MaxConns int }func NewDB(cfg DBConfig) DB { return &realDB{ addr: cfg.Addr, timeout: cfg.Timeout, maxConns: cfg.MaxConns, } }
- 进阶方式:用
Option函数type Option func(*DBConfig)
func WithTimeout(t time.Duration) Option { return func(c *DBConfig) { c.Timeout = t } }
func NewDB(opts ...Option) DB { cfg := &DBConfig{Timeout: 5 * time.Second} for _, opt := range opts { opt(cfg) } return &realDB{timeout: cfg.Timeout} }
- 避免把所有参数塞进一个大 map 或 interface{},那会失去编译期检查和 IDE 支持
如何避免工厂函数变成“上帝对象”入口
一个工厂函数负责创建多种不相关类型(比如同时造 DB、Cache、Logger),会导致职责混乱、难以测试、依赖爆炸。应该按领域或生命周期拆分。
立即学习“go语言免费学习笔记(深入)”;
- 错误示范:
NewSystemComponent(type string, cfg interface{}) interface{}—— 类型字符串 + 类型断言,运行时才报错 - 正确做法:每个关注点独立工厂
func NewDatabase(cfg DBConfig) Database { ... } func NewCache(cfg CacheConfig) Cache { ... } func NewLogger(cfg LogConfig) Logger { ... } - 需要统一初始化?用组合结构体 + 构造方法,而不是塞进一个工厂
type App struct { DB Database Cache Cache Logger Logger }func NewApp(cfg AppConfig) *App { return &App{ DB: NewDatabase(cfg.DB), Cache: NewCache(cfg.Cache), Logger: NewLogger(cfg.Log), } }
真正难的不是写出一个 NewXXX() 函数,而是判断哪些创建逻辑值得封装、哪些接口边界该划在哪儿、以及当配置变多时,要不要从结构体参数升级到 Option 模式 —— 这些没法靠模板解决,得看具体依赖关系和演进节奏。










