工厂模式封装对象创建,依赖注入实现解耦。通过工厂生成Logger实例,由DI将依赖注入UserService,主函数负责装配,结合两者提升可维护性与测试性,扩展时不改业务代码。

在Go语言开发中,工厂模式与依赖注入(DI)的结合能有效提升代码的可维护性与可测试性。通过工厂创建对象,再借助依赖注入管理组件之间的依赖关系,可以让程序结构更清晰,降低耦合度。下面从实际场景出发,介绍如何将两者结合使用。
工厂模式解决对象创建问题
当系统中存在多种类型的服务或组件,且创建逻辑较复杂时,直接在业务代码中初始化会导致重复和紧耦合。工厂模式封装了对象的创建过程。
例如,我们有一个日志接口,支持不同实现:
type Logger interface {
Log(msg string)
}
type FileLogger struct{}
func (f *FileLogger) Log(msg string) {
fmt.Println("File log:", msg)
}
type ConsoleLogger struct{}
func (c *ConsoleLogger) Log(msg string) {
fmt.Println("Console log:", msg)
}
通过工厂来决定返回哪种日志实现:
立即学习“go语言免费学习笔记(深入)”;
type LoggerFactory struct{}
func (f *LoggerFactory) Create(loggerType string) Logger {
switch loggerType {
case "file":
return &FileLogger{}
case "console":
return &ConsoleLogger{}
default:
return &ConsoleLogger{}
}
}
依赖注入实现组件解耦
有了工厂后,不应让业务模块直接调用工厂创建实例。而是通过依赖注入,将构建好的对象传递进去,避免模块感知创建逻辑。
比如一个用户服务依赖日志器:
type UserService struct {
logger Logger
}
func NewUserService(logger Logger) *UserService {
return &UserService{logger: logger}
}
func (s *UserService) Register(name string) {
s.logger.Log("User registered: " + name)
}
构造函数接收 Logger 接口,而不是内部自己 new 或调用工厂。这正是依赖注入的核心:由外部提供依赖。
组合工厂与依赖注入的初始化流程
真正的结合体现在应用启动阶段。在这个阶段,先通过工厂创建具体实例,然后注入到需要它的服务中。
主函数或初始化模块负责“装配”:
func main() {
factory := &LoggerFactory{}
logger := factory.Create("console") // 或从配置读取
userService := NewUserService(logger)
userService.Register("Alice")
}
这种方式下,UserService 完全不知道日志是如何创建的,只关心它能用。如果未来要加数据库日志,只需扩展工厂,不改业务代码。
进阶:使用依赖注入框架简化管理
当项目变大,手动管理依赖会变得繁琐。可以引入轻量级DI库如 google/wire 或 uber/fx 来自动化装配。
以 Wire 为例,定义 Provider 集合:
func ProvideLogger() Logger {
factory := &LoggerFactory{}
return factory.Create("file")
}
func ProvideUserService() *UserService {
return NewUserService(ProvideLogger())
}
Wire 在编译期生成注入代码,性能好且无运行时反射开销。
基本上就这些。工厂模式负责“造什么”,依赖注入决定“怎么给”。两者配合,让Go程序在保持简洁的同时具备良好的扩展性与测试便利性。不复杂但容易忽略。










