第一章:ASP.NET Core DI 中工厂模式的核心概念
在 ASP.NET Core 的依赖注入(DI)体系中,工厂模式是一种用于控制服务实例创建过程的重要设计模式。它允许开发者通过委托或专用工厂类来按需生成服务实例,从而实现更灵活的生命周期管理与条件化实例构造。
工厂模式的基本作用
工厂模式主要用于解耦服务的定义与实例化逻辑。在某些场景下,服务的创建需要复杂的初始化逻辑、运行时参数或上下文判断,直接使用内置的 DI 容器注册方式难以满足需求。此时,通过工厂模式可以封装这些复杂性。
- 延迟创建服务实例,避免不必要的资源消耗
- 根据运行时条件返回不同的实现类型
- 集成第三方库中无法直接注册的服务
使用 Func 工厂委托
ASP.NET Core 内置支持
Func<T> 类型的工厂注入。容器会自动解析匹配的委托,调用时返回对应实例。
// 注册服务
services.AddTransient<IService, ServiceA>();
// 在构造函数中注入工厂
public class Consumer
{
private readonly Func<IService> _serviceFactory;
public Consumer(Func<IService> serviceFactory)
{
_serviceFactory = serviceFactory;
}
public void Execute()
{
var service = _serviceFactory(); // 按需创建实例
service.DoWork();
}
}
自定义工厂接口
对于更复杂的场景,推荐定义显式的工厂接口:
| 方法 | 用途说明 |
|---|
| Create(string type) | 根据类型标识返回对应服务实现 |
| IsValid(string type) | 校验输入是否支持的类型 |
这种方式提升了代码可读性与可测试性,适用于多实现分支的业务场景。
第二章:基于工厂模式的动态服务解析
2.1 工厂模式与依赖注入容器的协同机制
在现代应用架构中,工厂模式与依赖注入(DI)容器的结合实现了对象创建与使用的解耦。工厂负责封装复杂实例化逻辑,而 DI 容器通过配置管理依赖关系,在运行时将工厂生成的实例自动注入目标组件。
职责分离与协作流程
DI 容器不直接创建对象,而是委托工厂完成初始化。容器维护工厂注册表,并根据请求调用对应工厂方法。
type ServiceFactory struct{}
func (f *ServiceFactory) CreateService(env string) Service {
if env == "prod" {
return &ProductionService{}
}
return &MockService{}
}
上述工厂根据环境变量生成不同服务实例。DI 容器注入该工厂后,可在需要时动态获取适配当前上下文的服务实现。
注册与解析过程
- 工厂实例注册至 DI 容器,标记为特定接口的提供者
- 容器在解析依赖时,调用工厂方法获取实例
- 生命周期管理由容器统一控制,确保单例或作用域实例正确复用
2.2 使用 Func 工厂实现按需服务获取
在依赖注入体系中,有时需要延迟创建服务实例,仅在真正使用时才触发初始化。此时可借助 `Func` 工厂模式实现按需获取。
Func 工厂的基本用法
通过将服务注册为 `Func`,容器返回一个委托,调用时才解析实例:
services.AddTransient<IDataService, DataService>();
services.AddSingleton<Func<IDataService>>(provider =>
() => provider.GetService<IDataService>());
上述代码注册了一个工厂委托,每次调用 `func()` 时才会创建新的 `DataService` 实例,适用于耗资源或条件性使用的场景。
适用场景与优势
- 避免启动时大量实例化,提升性能
- 支持条件分支中动态获取服务
- 解耦服务生命周期与使用者的绑定
2.3 基于命名服务的工厂注册与解析策略
在分布式架构中,工厂实例的动态发现与绑定依赖于高效的命名服务机制。通过将工厂实现注册至统一的命名目录,客户端可基于逻辑名称完成透明化解析。
注册流程设计
工厂类在启动时向命名服务提交元数据,包括唯一标识、网络地址及支持的接口版本。注册信息以持久化或临时节点形式存储,确保高可用性。
// 示例:gRPC 工厂注册逻辑
func RegisterFactory(name string, endpoint string) error {
conn, _ := grpc.Dial("naming-service:50051", grpc.WithInsecure())
client := pb.NewNamingClient(conn)
_, err := client.Register(context.Background(), &pb.Service{
Name: name,
Address: endpoint,
Type: "factory",
})
return err
}
上述代码将工厂服务注册至中心化命名节点,参数
name 用于后续解析定位,
Address 指明实际调用端点。
解析与负载策略
客户端通过名称查询命名服务,获取匹配的工厂实例列表,并结合健康状态与权重实施本地负载选择。
| 字段 | 含义 |
|---|
| ServiceName | 工厂逻辑名称 |
| Endpoints | 可用网络地址集合 |
| TTL | 注册有效期(秒) |
2.4 利用 IServiceProvider 构建运行时决策工厂
在依赖注入场景中,
IServiceProvider 不仅用于解析服务实例,还可作为运行时决策工厂的核心组件。通过动态解析策略接口的不同实现,实现灵活的行为调度。
基于条件的服务解析
例如,根据运行时配置选择不同的处理器:
public interface IPaymentProcessor
{
void Process(decimal amount);
}
public class CreditCardProcessor : IPaymentProcessor { /* 实现 */ }
public class PayPalProcessor : IPaymentProcessor { /* 实现 */ }
注册多个实现后,可在运行时按需解析:
var provider = serviceProvider;
var processor = context.PaymentMethod == "CreditCard"
? provider.GetRequiredService<CreditCardProcessor>()
: provider.GetRequiredService<PayPalProcessor>();
processor.Process(amount);
该方式利用容器已构建的服务图谱,避免手动实例化,提升可测试性与解耦程度。
策略注册表优化
使用字典封装类型映射,结合
IServiceProvider 实现轻量级工厂模式,既保留 DI 优势,又支持动态决策。
2.5 避免内存泄漏:工厂中作用域服务的正确释放
在依赖注入容器中,工厂模式常用于按需创建服务实例。若未正确管理作用域服务的生命周期,可能导致对象长期驻留内存,引发泄漏。
服务释放的典型场景
当工厂返回实现了
Dispose 接口的对象时,调用方有责任确保其被释放。
public interface IService : IDisposable { }
public class ServiceFactory {
public IService Create() => new ScopedService();
}
上述代码中,
ScopedService 实现了
IDisposable,但工厂本身不负责释放。调用方必须显式调用
Dispose()。
推荐的资源管理方式
- 使用
using 语句块自动释放资源 - 在作用域结束时统一释放所有服务
using var service = factory.Create();
service.Process();
// 离开 using 块时自动释放
该模式确保即使发生异常,也能正确释放非托管资源,防止内存泄漏。
第三章:策略模式与工厂结合的高级应用
3.1 定义业务策略接口并与工厂解耦
在复杂的业务系统中,策略模式能有效分离变化的行为。通过定义统一的策略接口,各类业务逻辑实现该接口,确保调用方依赖抽象而非具体实现。
策略接口定义
type PricingStrategy interface {
Calculate(price float64) float64
}
该接口声明了价格计算的通用方法,所有具体策略(如折扣、满减)需实现此方法,从而满足多态性要求。
工厂创建策略实例
- 工厂函数根据类型返回对应的策略实现
- 调用方无需知晓具体类,仅通过接口交互
- 新增策略时,只需实现接口并注册到工厂,符合开闭原则
通过接口抽象与工厂模式结合,系统具备良好的扩展性与可维护性,核心逻辑不再紧耦合于具体业务规则。
3.2 自动发现并注册策略实现类
在微服务架构中,策略模式常用于解耦业务逻辑与具体实现。为避免手动注册每个策略类,可采用自动发现机制。
服务扫描与接口绑定
通过反射或依赖注入框架扫描指定包路径下实现统一接口的类,并自动注册到策略管理器中。
@Component
public class StrategyRegistry {
private final Map<String, Strategy> strategies = new HashMap<>();
public void register(Strategy strategy) {
strategies.put(strategy.getType(), strategy);
}
public Strategy get(String type) {
return strategies.get(type);
}
}
上述代码定义了一个策略注册中心,
register 方法用于注入具体策略实例,
get 方法根据类型获取对应实现。
自动化加载流程
- 应用启动时扫描带有特定注解的类(如 @StrategyComponent)
- 实例化后调用 register 方法完成注册
- 运行时通过统一入口动态调用策略
3.3 运行时根据条件选择策略的工厂实现
在复杂业务场景中,需在运行时动态选择处理策略。策略工厂模式通过封装创建逻辑,实现解耦与可扩展性。
策略接口定义
type PaymentStrategy interface {
Process(amount float64) error
}
该接口声明了所有支付策略的统一行为,便于后续扩展与依赖抽象。
工厂核心实现
func NewPaymentStrategy(paymentType string) PaymentStrategy {
switch paymentType {
case "credit":
return &CreditCardStrategy{}
case "paypal":
return &PayPalStrategy{}
default:
return &DefaultStrategy{}
}
}
工厂函数根据传入类型返回对应策略实例,调用方无需感知具体实现。
- 支持新增支付方式而无需修改现有代码
- 符合开闭原则,提升系统可维护性
第四章:工厂模式在跨边界场景中的实践
4.1 在中间件中使用工厂注入领域服务
在现代分层架构中,中间件常用于处理横切关注点,如日志、认证和事务管理。通过工厂模式注入领域服务,可实现解耦与动态实例化。
工厂模式定义
工厂负责根据运行时条件创建领域服务实例,避免硬编码依赖。
type ServiceFactory interface {
CreateUserService() UserService
}
type serviceFactory struct {
db *sql.DB
}
func (f *serviceFactory) CreateUserService() UserService {
return NewUserServiceImpl(f.db)
}
上述代码中,
serviceFactory 实现了
ServiceFactory 接口,将数据库连接作为共享依赖注入到服务中,确保资源复用。
中间件集成
中间件通过工厂获取服务实例,执行业务逻辑:
- 请求到达时,调用工厂方法获取服务
- 执行领域操作,如用户权限校验
- 释放资源,保持无状态特性
该设计提升可测试性与扩展性,支持多租户场景下的服务隔离。
4.2 控制器内通过工厂延迟创建高成本服务
在控制器中直接实例化高成本服务(如数据库连接、大型缓存对象)会导致请求响应变慢,影响系统吞吐量。通过工厂模式实现延迟初始化,可将对象创建推迟到真正需要时。
工厂接口定义
type ServiceFactory interface {
GetExpensiveService() *ExpensiveService
}
该接口抽象了服务获取逻辑,避免控制器与具体实现耦合。
延迟创建实现
- 首次调用时才实例化服务对象
- 后续调用复用已创建实例
- 利用 sync.Once 保证线程安全
func (f *factory) GetExpensiveService() *ExpensiveService {
f.once.Do(func() {
f.service = newExpensiveService()
})
return f.service
}
once.Do 确保高成本初始化仅执行一次,提升并发性能。
4.3 结合配置选项动态构建客户端服务实例
在微服务架构中,客户端实例的创建往往需要根据运行时配置进行动态调整。通过注入配置参数,可实现灵活的服务地址、超时策略和重试机制定制。
配置驱动的客户端构建
使用结构体封装客户端配置项,支持从环境变量或配置文件加载:
type ClientConfig struct {
BaseURL string `json:"base_url"`
Timeout time.Duration `json:"timeout"`
MaxRetries int `json:"max_retries"`
}
该结构体定义了基础 URL、请求超时时间和最大重试次数,便于集中管理连接行为。
动态实例化流程
- 解析配置源(如 JSON、YAML 或环境变量)
- 校验必填字段有效性
- 依据配置初始化 HTTP 客户端与中间件链
最终返回一个符合接口约定的服务客户端,提升系统可配置性与测试便利性。
4.4 在后台任务(BackgroundService)中应用服务工厂
在 .NET 中,
BackgroundService 是构建长时间运行后台任务的理想选择。结合服务工厂模式,可以实现按需创建和管理服务实例,避免生命周期冲突。
动态服务实例创建
通过
IServiceScopeFactory 创建独立作用域,确保每次执行都获取全新的服务实例:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using var scope = _serviceScopeFactory.CreateScope();
var processor = scope.ServiceProvider.GetRequiredService();
await processor.ProcessAsync(stoppingToken);
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}
上述代码中,
_serviceScopeFactory 由依赖注入容器注入,
CreateScope() 方法生成独立服务作用域,防止服务实例跨线程共享,提升并发安全性。
应用场景与优势
- 适用于消息轮询、数据同步等周期性任务
- 避免
Scoped 服务在单例背景服务中的生命周期错误 - 实现资源的按需分配与及时释放
第五章:从实践到架构:工厂模式的最佳演进路径
识别创建逻辑的重复点
在多个服务类中频繁出现相似的对象初始化代码时,是引入工厂模式的明确信号。例如,在支付系统中,不同渠道(微信、支付宝)的客户端初始化逻辑高度相似,但分散在各业务方法中。
- 重复的配置加载
- 条件判断分支过多
- 对象构造过程复杂且易出错
封装简单工厂
将创建逻辑集中到独立函数中,降低调用方耦合度:
func NewPaymentClient(channel string) PaymentClient {
switch channel {
case "wechat":
return &WeChatClient{Config: loadConfig("wechat")}
case "alipay":
return &AlipayClient{Config: loadConfig("alipay")}
default:
panic("unsupported channel")
}
}
向抽象工厂演进
当系统需同时创建一组相关对象(如客户端 + 日志适配器),应升级为抽象工厂模式:
| 产品族 | 支付客户端 | 日志处理器 |
|---|
| 微信 | WeChatClient | WeChatLogger |
| 支付宝 | AlipayClient | AlipayLogger |
结合依赖注入实现解耦
通过 DI 容器注册工厂函数,运行时动态解析实例类型,提升测试性与扩展性。在 Go 中可使用 wire 或 dig 等工具完成自动装配,避免手动 new 调用遍布代码库。