如何在运行时实现多个策略的使用
本文关键字:策略 运行时 实现 | 更新日期: 2023-09-27 17:56:45
我需要处理从服务返回的记录列表。
但是,记录的处理算法完全根据记录上的某个字段而更改。
为了实现这一点,我定义了一个只有一个方法的IProcessor接口:
public interface IProcessor
{
ICollection<OutputEntity> Process(ICollection<InputEntity>> entities);
}
我有两个具体的IProcessor
实现,用于不同类型的处理。
问题是我需要同时使用IProcessor
的所有实现..那么如何将IProcessor
注入到我的引擎类中,从而驱动整个事情:
public class Engine
{
public void ProcessRecords(IService service)
{
var records = service.GetRecords();
var type1Records = records.Where(x => x.SomeField== "Type1").ToList();
var type2Records = records.Where(x => x.SomeField== "Type2").ToList();
IProcessor processor1 = new Type1Processor();
processor.Process(type1Records);
IProcessor processor2 = new Type2Processor();
processor.Process(type2Records);
}
}
这就是我目前正在做的事情......它看起来并不漂亮和干净。
关于我如何改进这个设计的任何想法..也许使用 IoC ?
更改IProcessor
接口,并添加一个新功能:
public interface IProcessor
{
ICollection<OutputEntity> Process(InputEntity> entity);
bool CanProcess (InputEntity entity);
}
然后,您的代码不需要了解有关实现的任何信息:
foreach (var entity in entities) {
var processor = allOfMyProcessors.First(p=>p.CanProcess(entity));
processor.Process(entity);
}
您的处理器将发挥魔力:
public class Processor1 : IProcessor {
public bool CanProcess(InputEntity entity) {
return entity.Field == "field1";
}
}
这种方法的好处是,您可以从程序集等加载新的处理器,而无需主代码实现知道任何单个实现。
您可以将 SomeField 规范放在 IProcessor 实现中(您必须向 IProcessor 接口添加一个额外的字段),并根据您当前使用的处理器查找相应的记录。
一些代码来清除它:
public interface IProcessor
{
ICollection<OutputEntity> Process(ICollection<InputEntity>> entities);
string SomeField{get;set;}
}
public class Engine
{
public Engine(IEnumerable<IProcessor> processors)
{
//asign the processors to local variable
}
public void ProcessRecords(IService service)
{
// getRecords code etc.
foreach(var processor in processors)
{
processor.Process(typeRecords.Where(typeRecord => typeRecord.SomeField == processor.SomeField));
}
}
}
或者,您可以在 ProcessRecords 方法中提供 IProcessors,或者将它们设置为 Engine 类中的属性(尽管我更喜欢构造函数注入)。
编辑
您可能还想在其他答案中研究CanProcess方法。尽管原理相同,但如果您需要更改条件以决定处理器是否应处理类型,它提供了更具可扩展性/健壮性的解决方案。
就我个人而言,我可能会制作一个处理不同记录类型的 IProcessor 实现。类似的东西
public class ProcessorImpl : IProcessor
{
// Either create them here or get them from some constructor injection or whatever.
private readonly Type1Processor type1 = new Type1Processor();
private readonly Type2Processor type2 = new Type2Processor();
public ICollection<OutputEntity> Process(ICollection<InputEntity>> entities)
{
var type1Records = records.Where(x => x.SomeField== "Type1").ToList();
var type2Records = records.Where(x => x.SomeField== "Type2").ToList();
var result = new List<OutputEntity>();
result.AddRange(type1.Process(type1Records));
result.AddRange(type2.Process(type2Records));
return result;
}
}
然后,您可以将所有输入实体传递给 Process 方法,而不必担心它包含哪些记录类型。
这个实现缺乏一些可扩展性,所以如果需要,它必须被扩展(参见Komet的答案)。基本思想是让一个单独的服务负责选择流程实现。
这可能看起来有点复杂,但您可以使用属性来标记处理器,并在运行时读取属性并查看您拥有的处理器并从中构建字典:
public interface IProcessor
{
ICollection<OutputEntity> Process(ICollection<InputEntity>> entities);
}
[Processor("Type1")]
public class Processor1 : IProcessor
{
}
[Processor("Type2")]
public class Processor1 : IProcessor
{
}
public class Engine
{
Dictionary<string, IProcessor> processors;
public Engine()
{
// use reflection to check the types marked with ProcessorAttribute and that implement IProcessor
// put them in the processors dictionary
// RegisterService(type, processor);
}
public RegisterService(string type, IProcessor processor)
{
processor[type] = processor;
}
public void ProcessRecords(IService service)
{
var records = service.GetRecords();
foreach(var kvp in processors)
{
kvp.Value.Process(records.Where(record => record.SomeField == kvp.Key));
}
}
}