工厂函数返回接口时必须返回具体类型实例,而非接口变量;需按方法接收者类型选择值或指针返回;字符串参数控制逻辑须用switch全覆盖并设default;接口实现需签名完全一致;依赖应显式注入而非闭包隐藏。

工厂函数返回接口类型时,必须确保返回值是具体类型的实例
Go 没有类继承,但可以用接口 + 具体类型组合实现多态。工厂函数的返回类型应声明为接口,但内部 return 的必须是实现了该接口的结构体实例(不能是接口变量本身,否则会丢失方法集)。常见错误是工厂里先赋值给中间接口变量再返回,导致编译通过但运行时方法调用 panic。
- ✅ 正确写法:
return &FileLogger{...}或return ConsoleLogger{...} - ❌ 错误写法:
var l Logger = &FileLogger{...}; return l(此时l是接口变量,若Logger有指针接收者方法,l无法调用) - 注意:如果接口方法由指针接收者定义,工厂必须返回指针(
&T{});若全为值接收者,T{}也可行
用字符串参数控制创建逻辑时,需显式处理未知类型
工厂常通过 type 字符串决定返回哪种实现,但 Go 没有反射式动态加载,所有分支必须硬编码。遗漏 default 或未覆盖的 case 会导致零值返回(如 nil 接口),后续调用直接 panic。
- 务必在
switch后加default分支,并返回明确错误或 panic 提示(例如panic("unknown logger type: " + typ)) - 避免用
map[string]Logger预注册——虽可行,但无法做编译期类型检查,且新增类型需同步改 map 初始化 - 推荐用
switch+ 具体构造表达式,每个分支清晰对应一种实现
接口方法签名不一致会导致工厂返回值无法满足接口
看似实现了接口,但因大小写、参数名、返回值顺序或类型细微差异(比如 error 写成 *errors.Error),编译器不会自动匹配。工厂函数返回值会被静态检查,不满足就报错。
- 检查接口定义和结构体方法签名是否完全一致:包括大小写、参数类型(
[]byte和string不兼容)、返回值数量与类型 - 常用技巧:在结构体方法上加
//go:noinline注释并故意写错签名,看编译错误提示——它会明确告诉你哪条方法不匹配 - 导出方法名首字母必须大写,否则包外无法调用,接口也无法被满足
带依赖注入的工厂需暴露构造参数,而非隐藏在闭包中
如果具体实现依赖外部资源(如 *os.File、http.Client),工厂函数应接收这些依赖作为参数,而不是在函数内 new 出来。否则测试难 mock,行为不可控。
- ❌ 不推荐:
func NewLogger(typ string) Logger { f, _ := os.Open("app.log"); return &FileLogger{f} } - ✅ 推荐:
func NewLogger(typ string, w io.Writer) Logger { ... }或更细粒度:func NewFileLogger(f *os.File) Logger - 多个依赖时,用配置结构体传参比长参数列表更可维护,例如
type LoggerConfig struct { Writer io.Writer; Level string }











