Golang工厂方法模式实践_多类型对象的灵活扩展

2次阅读

工厂方法通过统一接口和注册机制解耦对象创建逻辑,新增类型只需实现接口并调用registerparser,避免散落newxxx()和多文件修改。

Golang工厂方法模式实践_多类型对象的灵活扩展

go 里怎么用工厂方法解耦对象创建逻辑

工厂方法不是为了炫技,而是当你发现 NewXXX() 散落在十几处、每次加个新类型都要改七八个文件时,它才真正有用。核心是把“谁来造”和“造什么”分开,让新增类型只改一个地方。

典型场景:解析不同格式的配置(jsonYAMLTOML),每种格式对应一个 ConfigParser 实现,但上层代码不关心具体类型。

  • 定义统一接口:type Parser Interface { Parse([]byte) (map[String]interface{}, Error) }
  • 每个实现类型独立包或文件,不互相 import
  • 工厂函数返回接口,不暴露具体类型:func NewParser(format string) (Parser, error)
  • 避免在工厂里写大段 switch —— 把注册逻辑抽成 RegisterParser 函数,主程序启动时集中注册

为什么不用 map[string]func() Parser 直接注册

看起来简单,但容易踩两个坑:类型擦除后无法做编译期校验;热加载或插件化时,map 的键名拼错就静默失败。更稳的做法是用函数变量注册,靠 Go 类型系统兜底。

比如:

立即学习go语言免费学习笔记(深入)”;

var parserCreators = make(map[string]func() Parser)  func RegisterParser(name string, creator func() Parser) { 	if _, exists := parserCreators[name]; exists { 		panic("duplicate parser name: " + name) 	} 	parserCreators[name] = creator }  func NewParser(format string) (Parser, error) { 	creator, ok := parserCreators[format] 	if !ok { 		return nil, fmt.Errorf("unknown parser format: %s", format) 	} 	return creator(), nil }
  • 注册时传入 func() Parser,编译器会检查返回值是否满足 Parser 接口
  • 调用 creator() 是运行时行为,但构造逻辑本身仍受类型约束
  • 注意:不要在 init() 里注册——测试时难 mock,且跨包初始化顺序不可控

工厂方法和依赖注入容器冲突吗

不冲突,但职责要分清。工厂方法负责「同类对象的多态创建」,比如一 Parser;DI 容器负责「跨层级依赖组装」,比如把 Parser 塞进 Service。混用时最容易出问题的是生命周期管理。

  • 如果 DI 容器管理的是单例 Parser,那工厂方法就退化为配置路由,别再让它 new 实例
  • 如果每次都需要新实例(如带不同选项的 HTTPClient),工厂方法仍是首选,DI 容器应把工厂本身注入进去,而不是注入一堆具体类型
  • 警惕:用 digwire 自动生成工厂代码时,生成的函数签名可能漏掉错误返回,导致 panic 而非可控错误

扩展新类型时最常漏掉的三件事

加完 StructParse 方法,跑测试通过就提交?大概率线上出问题。

  • 忘记调用 RegisterParser("new_format", func() Parser { return &NewFormatParser{} })
  • 新类型实现了接口,但没导出字段或方法——比如 type NewFormatParser struct{ data map[string]interface{} }data 小写,反序列化失败却无提示
  • 没加单元测试覆盖工厂路由逻辑,只测了具体实现。结果 NewParser("new_format") 返回 nil,上游 panic

工厂方法真正的复杂点不在写法,而在注册时机、错误传播路径、以及团队对“谁负责注册”的约定是否清晰。没人管注册,再多的模式也只是空架子。

text=ZqhQzanResources