如何使用Golang构建工厂模式_Golang工厂模式创建对象示例

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

Go 语言没有类和继承,所谓“工厂模式”不是照搬 Java 那套抽象工厂 + 接口实现的模板,而是用 funcinterface{} 和结构体组合来达成「封装创建逻辑、解耦使用者与具体类型」的目的。直接写 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),会导致职责混乱、难以测试、依赖爆炸。应该按领域或生命周期拆分。

  • 错误示范: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 模式 —— 这些没法靠模板解决,得看具体依赖关系和演进节奏。