Как реализовать использование нескольких стратегий во время выполнения - PullRequest
7 голосов
/ 17 мая 2011

Мне нужно обработать список записей, возвращаемых службой.
Однако алгоритм обработки записи полностью изменяется в зависимости от определенного поля в записи.
Чтобы реализовать это, я определил интерфейс 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 голосов
/ 17 мая 2011

Измените свой 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";
    }
}

Преимущество этого подхода заключается в том, что вы можете загружать новые процессоры из сборок и т. Д. Без необходимости в реализации основного кода.

2 голосов
/ 17 мая 2011

Вы можете поместить спецификацию 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));
        }
    }
}

В качестве альтернативы, вы можете предоставить IP-процессоры в методе ProcessRecords или установить их как Свойства в классе Engine (хотя я предпочитаю внедрение конструктора).

Редактировать

Возможно, вы захотите взглянуть на подход CanProcess в других ответах.Хотя принцип тот же, он обеспечивает еще более расширяемое / надежное решение, если вам нужно изменить критерии, чтобы решить, должен ли процессор обрабатывать типы.

0 голосов
/ 17 мая 2011

Это может показаться немного сложным, но вы можете использовать атрибуты для маркировки ваших процессоров, а во время выполнения прочитать атрибуты и посмотреть, какой у вас процессор, и построить из него словарь:

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));
     }
  }  
}
0 голосов
/ 17 мая 2011

Лично я, вероятно, сделал бы одну реализацию 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, не беспокоясь о том, какие типы записей в нем содержатся.

В этой реализации отсутствует некоторая расширяемость, поэтому при необходимостибыть расширенным (см. ответ Кометы).Основная идея состоит в том, чтобы иметь один отдельный сервис, ответственный за выбор реализации процесса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...