第一章:ASP.NET Core DI 工厂模式的核心价值
在现代 ASP.NET Core 应用开发中,依赖注入(DI)是构建松耦合、可测试和可维护系统的核心机制。然而,当需要根据运行时条件动态创建服务实例时,标准的 DI 容器注册方式便显得力不从心。工厂模式结合 DI 容器,提供了优雅的解决方案,使对象的创建逻辑与使用逻辑分离,同时保留依赖注入的优势。
解决多实例场景的灵活性
当应用需要根据用户请求、配置或上下文创建不同实现的服务时,工厂模式可以封装这些决策逻辑。例如,在处理多种支付网关或数据导出格式时,通过工厂返回对应的服务实例,避免在业务代码中硬编码判断逻辑。
与内置 DI 容器的无缝集成
ASP.NET Core 的 DI 系统支持将工厂委托注册为服务,允许在运行时解析所需服务。以下代码展示了如何定义并注册一个日志服务工厂:
// 日志服务接口
public interface ILoggerService
{
void Log(string message);
}
// 工厂定义
public delegate ILoggerService LoggerServiceFactory(string type);
// 在 Program.cs 中注册
builder.Services.AddTransient<ConsoleLogger>();
builder.Services.AddTransient<FileLogger>();
builder.Services.AddSingleton<LoggerServiceFactory>(provider => key =>
{
return key switch
{
"console" => provider.GetService<ConsoleLogger>(),
"file" => provider.GetService<FileLogger>(),
_ => throw new ArgumentException("Unknown logger type")
};
});
上述方式使得服务的创建延迟到实际调用时刻,且由容器管理生命周期。
提升代码的可测试性与可扩展性
通过工厂模式,可以在单元测试中轻松替换特定实现,而无需修改核心逻辑。同时,新增服务类型仅需扩展工厂逻辑,符合开闭原则。
以下表格对比了直接注入与工厂模式的关键差异:
| 特性 | 直接注入 | 工厂模式 |
|---|
| 实例化时机 | 启动时确定 | 运行时动态决定 |
| 扩展性 | 低 | 高 |
| 测试友好度 | 中等 | 高 |
第二章:工厂模式的理论基础与设计思想
2.1 理解依赖注入与控制反转的本质
控制反转:从主动获取到被动接收
控制反转(IoC)是将对象的创建和管理权交由容器处理,而非由程序代码直接控制。传统模式中,组件主动实例化其依赖;而在IoC中,依赖关系由外部容器在运行时注入,实现了调用者与实现类之间的解耦。
依赖注入的实现方式
依赖注入(DI)是IoC的一种具体实现,常见形式包括构造函数注入、属性注入和方法注入。以Go语言为例,构造函数注入如下:
type Service struct {
repo Repository
}
func NewService(r Repository) *Service {
return &Service{repo: r}
}
上述代码中,
Service 不再自行创建
Repository 实例,而是通过构造函数由外部传入。这使得逻辑层与数据层彻底分离,便于替换实现和单元测试。
- 解耦组件间的创建依赖
- 提升可测试性与可维护性
- 支持灵活配置和动态替换
2.2 工厂模式在DI容器中的角色定位
解耦对象创建与使用
工厂模式在依赖注入(DI)容器中承担着核心的实例化职责。它将对象的创建过程封装起来,使应用程序代码无需关心具体实现类的构造细节,仅通过抽象接口获取服务实例。
动态实例生成策略
DI容器利用工厂模式根据配置或注解动态决定实例化哪个具体类。例如,在Go语言中可实现如下工厂函数:
func NewService(serviceType string) Service {
switch serviceType {
case "email":
return &EmailService{}
case "sms":
return &SMSService{}
default:
panic("unknown service type")
}
}
上述代码中,
NewService 函数根据传入参数返回不同实现,DI容器调用该工厂方法按需注入对应服务,实现了运行时多态性与配置驱动的灵活性。
2.3 IServiceFactory vs 自定义工厂的对比分析
在依赖注入框架中,
IServiceFactory 提供了标准化的服务解析机制,而自定义工厂则赋予开发者更精细的控制能力。
核心差异对比
| 维度 | IServiceFactory | 自定义工厂 |
|---|
| 生命周期管理 | 由容器统一管理 | 需手动处理 |
| 扩展性 | 受限于接口契约 | 高度灵活 |
典型使用场景
- IServiceFactory 适用于标准服务解析场景
- 自定义工厂适合复杂构造逻辑或条件实例化
public interface IServiceFactory
{
T GetService<T>();
}
该接口封装了服务获取逻辑,屏蔽底层容器细节。调用
GetService<T>() 时,内部通过依赖注入容器解析实例,确保生命周期正确。而自定义工厂可在创建前加入缓存、策略判断等增强逻辑。
2.4 生命周期管理在工厂中的关键影响
生命周期管理在智能制造中扮演着核心角色,有效贯通从设备接入、运行监控到退役回收的全过程。
提升设备可用性与维护效率
通过统一的生命周期策略,工厂可实现设备状态的实时追踪。例如,使用事件驱动架构监听设备状态变更:
// 设备状态变更处理逻辑
func HandleDeviceStateChange(event DeviceEvent) {
switch event.State {
case "online":
log.Info("设备上线,注册监控")
RegisterMetricsCollector(event.DeviceID)
case "maintenance":
TriggerMaintenanceWorkflow(event.DeviceID) // 触发维保流程
}
}
该代码段展示了如何根据设备状态自动执行对应操作,减少人工干预。
优化资源配置
- 设备在不同阶段(调试、生产、停用)动态分配资源
- 通过版本控制实现固件与配置的可追溯更新
- 降低因配置错误导致的停机风险
2.5 多实例场景下工厂模式的必要性
在分布式系统或多租户架构中,常需创建多个相似但配置不同的服务实例。若直接使用构造函数初始化对象,会导致代码重复、耦合度高且难以维护。
工厂模式的核心优势
- 封装对象创建逻辑,统一管理实例生命周期
- 支持按条件动态返回不同配置的实例
- 降低调用方与具体实现类的依赖
示例:数据库连接工厂
type DBFactory struct{}
func (f *DBFactory) CreateDB(dbType string) Database {
switch dbType {
case "mysql":
return &MySQL{Conn: "configured-mysql"}
case "redis":
return &Redis{Addr: "localhost:6379"}
default:
panic("unsupported db")
}
}
上述代码通过工厂方法屏蔽底层实例化细节,调用方无需知晓具体类型即可获取所需实例,显著提升扩展性与可测试性。
第三章:常见实现方式与代码实践
3.1 基于Func委托的轻量级工厂实现
在 .NET 中,`Func` 委托提供了一种无需定义接口或抽象类即可实现对象创建的简洁方式。通过将构造逻辑封装为 `Func` 类型的委托,可以构建灵活且低耦合的轻量级工厂。
核心实现机制
利用字典存储类型标识与对应创建委托的映射关系,按需实例化对象:
var factory = new Dictionary>
{
{ "Logger", () => new Logger() },
{ "Service", () => new BusinessService() }
};
var instance = factory["Logger"]();
上述代码中,每个键关联一个无参构造的 `Func