如何在运行时实现多个策略的使用

7

我需要处理从服务返回的记录列表。
然而,根据记录上某个特定字段的不同,记录的处理算法会完全改变。
为了实现这一点,我定义了一个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?

4个回答

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";
    }
}

这种方法的好处是,您可以从程序集等中加载新的处理器,而不需要您的主要代码实现了解任何单个存在的实现。


我也会这样做,但我会取出所有匹配的处理器而不仅仅是第一个(但这可能不符合OP的要求)。 - Shtong
非常感谢大家提供这么多答案..我想我可以采用所有这些方法。无论我采取什么方法..如何在Engine类中注入IEnumerable<Processor>? - Sennin

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类的属性中设置它们(虽然我更喜欢构造函数注入)。

编辑

您还可以查看其他答案中的CanProcess方法。虽然原则相同,但如果您需要更改决定处理器是否应该处理这些类型的标准,则提供了更加可扩展/健壮的解决方案。


谢谢KoMet,这比我的现有实现好多了。只有一个问题..如何注入IEnumerable<IProcessor>实例呢?我不想手动创建这些Processors。我想使用IoC(Unity / Castle)来实现..但是我会注册具有相同接口的多个类型..这样做可以吗? - Sennin
是的,这绝对是IoC库的典型用法。您需要在某个配置文件中定义所有的IProcessor实现,然后IoC应该能够使用这些实现的实例填充集合。 - Shtong
您可以通过在构造函数中手动传递它们(这已经是IoC),或者使用一整个IoC容器数组(Ninject,Unity等)来注入它们。它们有特定的IoC实现方式,可以通过配置文件或代码中的某些配置(例如:在global.asax或主方法中)或属性(例如:MEF)来实现。 - koenmetsu

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的答案)。基本思想是拥有一个单独的服务来负责选择处理实现。


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));
     }
  }  
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接