Go工厂模式用func+interface+结构体组合实现创建逻辑封装与解耦,核心是返回接口而非具体struct以支持实现替换,配置应通过结构体或Option函数传递,避免上帝工厂,按领域拆分职责。
Go 语言没有类和继承,所谓“工厂模式”不是照搬 Java 那套抽象工厂 + 接口实现的模板,而是用 func、interface{} 和结构体组合来达成「封装创建逻辑、解耦使用者与具体类型」的目的。直接写 new(MyStruct) 不叫工厂;返回接口、隐藏构造细节、支持配置化创建,才算。
工厂的核心价值是让调用方不依赖具体类型。如果工厂返回 *MyService,调用方就和这个 struct 绑死了;而返回一个定义好的 Service 接口,后续就能轻松替换实现(比如从内存版换成 Redis 版),无需改调用代码。
type Service interface {
Do() error
Close() error
}func NewService(cfg Config) Service,而非 func NewService() *ConcreteService
硬编码配置(如写死数据库地址)会让工厂不可复用。推荐用结构体传参,字段可导出供外部设置,也可加 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} }
一个工厂函数负责创建多种不相关类型(比如同时造 DB、Cache、Logger),会导致职责混乱、难以测试、依赖爆炸。应该按领域或生命周期拆分。
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 模式 —— 这些没法靠模板解决,得看具体依赖关系和演进节奏。