依赖注入环境下策略模式的运行时策略选择方案探讨
依赖注入环境下策略模式的运行时选择优化方案
针对你提到的DI容器中按TaskType枚举选择对应ICalculator的场景,除了你列出的两种方案,还有两种更符合SOLID原则且常见的规范实现方式:
1. 直接注入所有策略实现并筛选(推荐用于单例/范围生命周期的策略)
这种方式不需要修改原有ICalculator接口,也无需额外工厂类,利用DI容器支持注入所有服务实现集合的特性:
- 第一步:给每个
ICalculator实现类添加TaskType标识,可以用特性或者只读属性:// 用特性标记对应任务类型 [AttributeUsage(AttributeTargets.Class)] public class CalculatorTypeAttribute : Attribute { public TaskType Type { get; } public CalculatorTypeAttribute(TaskType type) => Type = type; } [CalculatorType(TaskType.Sum)] public class SumCalculator : ICalculator { public int Calculate(int a, int b) => a + b; } [CalculatorType(TaskType.Subtraction)] public class SubtractionCalculator : ICalculator { public int Calculate(int a, int b) => a - b; } - 第二步:在客户端注入
IEnumerable<ICalculator>,运行时通过筛选找到对应实例:public class CalculatorClient { private readonly IEnumerable<ICalculator> _calculators; public CalculatorClient(IEnumerable<ICalculator> calculators) { _calculators = calculators; } public int ExecuteCalculation(TaskType taskType, int a, int b) { var calculator = _calculators.FirstOrDefault(c => c.GetType().GetCustomAttribute<CalculatorTypeAttribute>()?.Type == taskType); if (calculator == null) throw new InvalidOperationException($"No calculator found for task type: {taskType}"); return calculator.Calculate(a, b); } }
这种方式的优势是遵循单一职责原则:ICalculator只负责计算逻辑,类型匹配逻辑由客户端处理,同时完全利用DI容器管理策略实例的生命周期。
2. 基于DI的轻量工厂模式(推荐用于瞬态生命周期的策略)
如果你的计算器需要每次请求都创建新实例(瞬态),可以优化原有工厂模式,让工厂依赖DI容器而非自行创建实例,避免手动管理对象生命周期:
- 定义工厂接口:
public interface ICalculatorFactory { ICalculator Create(TaskType taskType); } - 工厂实现类注入
IServiceProvider,从DI中获取对应实例:public class CalculatorFactory : ICalculatorFactory { private readonly IServiceProvider _serviceProvider; public CalculatorFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public ICalculator Create(TaskType taskType) { return taskType switch { TaskType.Sum => _serviceProvider.GetRequiredService<SumCalculator>(), TaskType.Subtraction => _serviceProvider.GetRequiredService<SubtractionCalculator>(), _ => throw new InvalidOperationException($"Unsupported task type: {taskType}") }; } } - 客户端注入
ICalculatorFactory即可获取对应计算器:public class CalculatorClient { private readonly ICalculatorFactory _factory; public CalculatorClient(ICalculatorFactory factory) { _factory = factory; } public int ExecuteCalculation(TaskType taskType, int a, int b) { var calculator = _factory.Create(taskType); return calculator.Calculate(a, b); } }
这种方式既保留了工厂模式的灵活性,又让DI容器全权负责对象的创建和生命周期管理,避免了手动new实例的弊端。
方案对比
- 你提到的修改
ICalculator添加CanHandleTask方法:虽然可行,但会让接口承担了计算之外的类型匹配职责,违反单一职责原则,且所有实现类都要重复实现该方法,冗余度高。 - 上述两种优化方案:更贴合DI的设计理念,同时遵循SOLID原则,可维护性和扩展性更强——比如新增
MultiplicationCalculator时,只需要添加标记特性或在工厂中新增分支(后者还可以通过配置或反射进一步解耦)。
内容的提问来源于stack exchange,提问作者Enrico Massone
相关产品推荐
相关产品推荐

