跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

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

工厂方法通过统一接口和注册机制解耦对象创建逻辑,新增类型只需实现接口并调用RegisterParser,避免散落NewXXX()和多文件修改。 Go 里怎么用工厂方法解耦对象创建逻辑 工厂方法不是为了炫技,而是当你发现
NewXXX()
散落在十几处、每次加个新类型都要改七八个文件时,它才真正有用。核心是把“谁来造”和“造什么”分开,让新增类型只改一个地方。 典型场景:解析不同格式的配置(
JSON
、
YAML
、
TOML
),每种格式对应一个
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 容器应把工厂本身注入进去,而不是注入一堆具体类型 警惕:用
dig
或
wire
自动生成工厂代码时,生成的函数签名可能漏掉错误返回,导致 panic 而非可控错误 扩展新类型时最常漏掉的三件事 加完 struct 和
Parse
方法,跑测试通过就提交?大概率线上出问题。 忘记调用
RegisterParser("new_format", func() Parser { return &NewFormatParser{} })
新类型实现了接口,但没导出字段或方法——比如
type NewFormatParser struct{ data map[string]interface{} }
,
data
小写,反序列化失败却无提示 没加单元测试覆盖工厂路由逻辑,只测了具体实现。结果
NewParser("new_format")
返回
nil
,上游 panic 工厂方法真正的复杂点不在写法,而在注册时机、错误传播路径、以及团队对“谁负责注册”的约定是否清晰。没人管注册,再多的模式也只是空架子。

相关文章