我需要处理从服务返回的记录列表 但是,记录的处理算法完全根据记录中的某个字段而变化 为了实现这一点,我已经定义了一个只有一个方法的IProcessor接口:
public interface IProcessor
{
ICollection<OutputEntity> Process(ICollection<InputEntity>> entities);
}
对于不同类型的处理,我有IProcessor
的两个具体实现
问题是我需要同时使用IProcessor
的所有实现..所以我如何将IProcessor
注入到我的Engine类中来驱动整个事情:
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?
答案 0 :(得分:5)
更改您的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";
}
}
这种方法的好处是你可以从程序集等加载新的处理器,而你的主代码实现不必知道那里的任何单个实现。
答案 1 :(得分:2)
你可以将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类中将它们设置为Properties(尽管我更喜欢构造函数注入)。
修改强>
您可能还想在其他答案中查看CanProcess方法。虽然原理是相同的,但如果您需要更改标准以决定处理器是否应该处理类型,它会提供更加可扩展/可靠的解决方案。
答案 2 :(得分:0)
就个人而言,我可能会制作一个处理不同记录类型的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的回答)。基本思想是让一个单独的服务负责选择流程实现。
答案 3 :(得分:0)
这可能看起来有点复杂,但您可以使用属性来标记处理器,并在运行时读取属性并查看您拥有的处理器并从中构建字典:
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));
}
}
}