应直接用newxxx,如newlogger、newprocessor,这是go社区事实标准;仅当初始化明显依赖特定输入源(如newconfigfromyaml)才加后缀,避免api爆炸和调用方记忆负担。

工厂函数名该用 NewXXX 还是 NewXXXFromYyy?
直接用 NewXXX,比如 NewLogger、NewProcessor。这是 Go 社区事实标准,net/http、database/sql、encoding/json 全都这么写。IDE 识别、文档生成、静态分析工具都依赖这个命名惯例。
只有当初始化逻辑明显依赖特定输入源时,才加后缀,比如 NewConfigFromYAML、NewClientFromEnv。但别搞成 NewLoggerFromFile + NewLoggerFromKafka + NewLoggerFromStdout —— 这会让调用方被迫记住一堆名字,违背“统一入口”初衷。
-
NewLogger应该能处理常见场景(如默认 stdout),复杂配置通过 option 参数传入 - 若必须区分来源,优先用字符串参数控制:
NewLogger("file", filename)或NewLogger("kafka", brokerAddr) - 后缀式命名容易导致 API 爆炸,且无法在运行时动态切换(比如 config.yaml 里写
logger.type: kafka,你得硬编码匹配到NewLoggerFromKafka)
map[string]func() Interface 注册表要不要放 init()?
小项目、无外部依赖时,放 init() 最省事;但只要构造函数可能用到配置、DB 连接或环境变量,就必须改成显式注册——否则 init() 阶段 panic 或静默失败,调试极难定位。
典型错误:在 init() 里调 sql.Open 或读 os.Getenv,结果 main 函数还没执行,init 就因 DB 地址为空挂了,堆栈还只显示 runtime.init。
- 推荐写一个
RegisterProcessor(name string, ctor func() Processor)函数,在main()开头或配置加载后集中调用 - 注册表变量本身要导出(如
var Processors = make(map[string]func() Processor)),方便测试中替换 mock - 别用
sync.Once包裹 map 初始化——它只防重复初始化,不防注册时机错乱
Factory 返回 *Struct 还是 Interface?
返回 Interface,不是 *Struct。工厂函数的契约是“给你一个能干活的对象”,不是“给你某个具体结构体的指针”。返回具体类型会锁死实现,破坏可替换性。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
常见反模式:func NewFileLogger() *fileLogger —— 调用方直接用了 *fileLogger 字段或未导出方法,后续想换成 rotatingFileLogger 就得改所有调用点。
- 接口定义要窄:只包含业务必需的方法,比如
Logger只要Info/Error,别塞SetLevel这种配置方法 - 结构体实现接口时,用值接收器还是指针接收器,取决于是否需要修改内部状态;但工厂返回值类型永远是接口
- 如果某结构体没实现完整接口(比如漏了
Error方法),编译器会立刻报错,这是 Go 的优势,别绕过它
Option 模式该不该在工厂里支持?
该,但只在初始化参数超过 2 个、或存在合理默认值时引入。简单对象(比如 Circle{Radius: 1.0})硬加 WithRadius 反而啰嗦。
Option 模式本质是把可选配置收拢成一组函数,避免 NewXxx(a, b, c, d, e, f, g, h) 这种参数地狱。但它不是银弹——滥用会导致工厂函数签名膨胀、option 执行顺序难追溯。
- 定义不可导出的
type option func(*T),每个 option 只做一件事(如WithTimeout、WithRetries) - option 函数体内禁止副作用:不能开 goroutine、不能打开文件、不能调远程服务——它只负责赋值
- 工厂函数最后按传入顺序应用 option,后传入的覆盖先传入的,这点必须文档化
最易被忽略的一点:option 模式和注册表模式不互斥。你可以用 NewProcessor("json", WithTimeout(5*time.Second)),也可以先注册 "json" 构造函数,再用 option 控制其行为。关键在分层清晰——注册解决“创建谁”,option 解决“怎么创建”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










