Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement usage of multiple strategies at runtime

I need to process a list of records returned from a service.
However the processing algorithm for a record changes completely based on a certain field on the record.
To implement this , I have defined an IProcessor interface which has just one method :

public interface IProcessor
{         
    ICollection<OutputEntity> Process(ICollection<InputEntity>> entities);
}

And I have two concrete implementations of IProcessor for the different types of processing.
The issue is that I need to use all implementations of IProcessor at the same time .. so how do I inject the IProcessor into my Engine class which drives the whole thing:

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

This is what I'm doing at present .. and it doesn't look nice and clean.
Any ideas as to how I could improve this design .. using IoC perhaps ?

like image 371
Sennin Avatar asked May 17 '11 10:05

Sennin


2 Answers

Change your IProcessor interface, and add a new function:

public interface IProcessor
{         
    ICollection<OutputEntity> Process(InputEntity> entity);
    bool CanProcess (InputEntity entity);
}

Then your code doesn't need to know anything about the implementation:

foreach (var entity in entities) {
    var processor = allOfMyProcessors.First(p=>p.CanProcess(entity));

    processor.Process(entity);
}

Your processor would do the magic:

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

Benefit of this approach is that you can load new processors from assemblies etc. without your main code implementation having to know about any single implementation out there.

like image 146
Jan Jongboom Avatar answered Nov 05 '22 02:11

Jan Jongboom


You could put the SomeField specification in the IProcessor implementations (you'd have to add an extra field to the IProcessor interface), and find the corresponding records based on the processor you're using at the moment.

A bit of code to clear that up:

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

Alternatively, you could provide the IProcessors in the ProcessRecords method, or set them as Properties in the Engine class ( though I prefer the constructor injection ).

Edit

You might also want to look into the CanProcess approach in the other answers. Though the principle is the same, it provides an even more extensible/robust solution if you need to change the criteria to decide if the processor should process the types.

like image 41
koenmetsu Avatar answered Nov 05 '22 02:11

koenmetsu