Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/307.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在运行时实现多种策略的使用_C#_Strategy Pattern - Fatal编程技术网

C# 如何在运行时实现多种策略的使用

C# 如何在运行时实现多种策略的使用,c#,strategy-pattern,C#,Strategy Pattern,我需要处理从服务返回的记录列表。 但是,记录的处理算法完全根据记录上的某个字段而改变。 为了实现这一点,我定义了一个IPProcessor接口,它只有一个方法: public interface IProcessor { ICollection<OutputEntity> Process(ICollection<InputEntity>> entities); } 这就是我目前正在做的。。而且它看起来不干净漂亮。 关于如何改进这个设计有什

我需要处理从服务返回的记录列表。
但是,记录的处理算法完全根据记录上的某个字段而改变。
为了实现这一点,我定义了一个IPProcessor接口,它只有一个方法:

public interface IProcessor
{         
    ICollection<OutputEntity> Process(ICollection<InputEntity>> entities);
}
这就是我目前正在做的。。而且它看起来不干净漂亮。

关于如何改进这个设计有什么想法吗。。也许使用IoC?

您可以将SomeField规范放在IPProcessor实现中(您必须向IPProcessor接口添加一个额外的字段),并根据当前使用的处理器查找相应的记录

下面是一段代码,可以澄清这一点:

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));
        }
    }
}
公共接口i处理器
{         
i收集过程(i收集>实体);
字符串SomeField{get;set;}
}
公共级引擎
{
公共引擎(IEnumerable处理器)
{
//将处理器指定为局部变量
}
公共档案(iSeries服务)
{
//获取记录代码等。
foreach(处理器中的var处理器)
{
Process(typeRecords.Where(typeRecord=>typeRecord.SomeField==processor.SomeField));
}
}
}
或者,您可以在ProcessRecords方法中提供IPProcessor,或者将它们设置为Engine类中的属性(尽管我更喜欢构造函数注入)

编辑


您可能还想在其他答案中探讨CanProcess方法。尽管原理相同,但如果您需要更改标准以决定处理器是否应处理类型,则它提供了一个更具扩展性/鲁棒性的解决方案。

更改
i处理器接口,并添加一个新功能:

public interface IProcessor
{         
    ICollection<OutputEntity> Process(InputEntity> entity);
    bool CanProcess (InputEntity entity);
}
您的处理器将发挥神奇的作用:

public class Processor1 : IProcessor {
    public bool CanProcess(InputEntity entity) {
        return entity.Field == "field1";
    }
}

这种方法的好处是,您可以从程序集等加载新处理器,而主代码实现不必知道任何单个实现。

我个人可能会制作一个处理不同记录类型的IPProcessor实现。差不多

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;
    }
}
公共类处理器mpl:ipprocessor
{
//要么在这里创建它们,要么从构造函数注入中获取它们。
私有只读类型1处理器类型1=新类型1处理器();
私有只读Type2Processor type2=新的Type2Processor();
公共ICollection进程(ICollection>实体)
{
var type1Records=records.Where(x=>x.SomeField==“Type1”).ToList();
var type2Records=records.Where(x=>x.SomeField==“Type2”).ToList();
var result=新列表();
result.AddRange(type1.Process(type1Records));
result.AddRange(type2.Process(type2Records));
返回结果;
}
}
然后,您可以将所有输入实体传递给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));
     }
  }  
}
公共接口i处理器
{         
i收集过程(i收集>实体);
}
[处理器(“类型1”)]
公共类处理器1:IPProcessor
{
}
[处理器(“类型2”)]
公共类处理器1:IPProcessor
{
}
公共级引擎
{
字典处理器;
公共引擎()
{
//使用反射检查用ProcessorAttribute标记并实现IPProcessor的类型
//把它们放到字典里
//注册服务(类型、处理器);
}
公共RegisterService(字符串类型,IPProcessor处理器)
{
处理器[类型]=处理器;
}
公共档案(iSeries服务)
{  
var records=service.GetRecords();
foreach(处理器中的var kvp)
{
进程(records.Where(record=>record.SomeField==kvp.Key));
}
}  
}

谢谢KoMet,这比我现有的实现要好得多。不过还有一个问题..如何注入IEnumerable实例。我不想手动创建这些处理器。我想用一个IoC(Unity/castle)来实现这一点。但是我会用同一个接口注册多个类型。这是允许的吗?是的,这绝对是IoC库的典型用法。您必须在某些配置文件中定义所有IPProcessor实现,并且IoC应该能够用实现的实例填充集合。您可以通过将它们传递到构造函数(已经是IoC)中来手动注入它们,也可以使用整个IoC容器数组(Ninject、Unity等)。他们有关于如何进行IoC的详细说明,可以通过配置文件或代码中的某些配置(例如:在global.asax或main方法中),也可以通过属性(例如:MEF)。我也会这样做,但我会选择所有匹配的处理器,而不仅仅是第一个(但可能不符合OP的要求),非常感谢so may的回答。。我想我可以接受所有这些。不管我采取什么方法。。如何在Engine类中注入IEnumerable?
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;
    }
}
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));
     }
  }  
}