C# – Strategy Pattern Not Sufficient for Problem?

cdelegatesdesign-patterns

Let me sketch the situation:

  • I have multiple users, with certain properties (2 enums)
  • For each user I need to fetch data, for some with some basic filtering, for some extended filtering (= basic filtering + extra filtering). I'd like to do that not separate for every user, but I'd rather group the users and do it in two queries.
  • For every user, I need to filter that data depending on the values of the enums. I will always need to do GetFirstData() (method depending on first enum), GetLastData() (method depending on second enum), CheckData() (depending on both enums).

I've been looking at the Strategy Pattern, but it seems that's more designed to implement one behavior. I want to combine my behaviors to avoid making the combinations between all GetFirstData and GetLastData, is there any pattern to do this better? I've been thinking on just using 2 delegates and assign the corresponding methods depending on the values of the enums. Would this be the cleanest way?

Little example of what I mean:

public class User
{
   public Enum1 FirstEnum {get; set;}
   public Enum2 SecondEnum {get; set;}
   ...
}
public IEnumerable<Data> Filter(int userId, Expression extraFilter)
{
    var data = GetData(userId);
    if(extraFilter != null)
      data = data.Where(extraFilter);
    return data;
}
public Data GetFirstData(IEnumerable<Data> data);
public Data GetLastData(IEnumerable<Data> data);
public bool CheckData(IEnumerable<Data> data);

My endresult could do something like this:

public class EndResult
{
    public Data FirstResult {get; set;}
    public Data SecondResult {get; set;}
    public Func<IEnumerable<Data>,Data> GetFirstData {get; set;}
    public Func<IEnumerable<Data>,Data> GetLastData {get; set;}
    public bool ExtendeFiltering {get; set;}

    public EndResult(User user)
    {
        switch(user.enum1)
        {
            case: GetFirstData = specificFunction;
                  ExtendedFiltering = true;
            ...
        }
        //Second for GetLastData;
    }

    public void Execute()
    {
        GetData();
        CheckData();
        GetFirstData();
        GetLastData();
    }
}

Edit: For future readers who are curious, I didn't use delegates (not directly at least). I created 2 interface IFirst and ILast with a corresponding method. In my static create method defined on my processor class, I do the logic to create an instance of those interfaces based on certain conditions. The reason I left the path of directly using delegates is because it turned out I needed more parameters than just User for some of them. So I resorted to different implementations based on the parameters I need in the constructor of those classes.

Best Answer

Using delegates in the suggested manner is a specific form of the strategy pattern which can always be used if the strategy objects are as simple as a function (see here, for example).

However, the essence of your code is that you have an object of type EndResult which encapsulates how a set of operations interact (if the operations are given by strategy objects or "strategy delegates" does not matter). This is called Mediator pattern, the Mediator here provides what is called "context" in this description of the strategy pattern.

Deciding between the different strategies will typically be the task of a factory method. If you make that method part of your mediator (as in your example, in the constructor of the EndResult), or if you prefer to delegate this to another class, is something you need to decide by yourself. It depends on things of the overall size and structure of that class and the surroundings, and how much "separation of concerns" you really need. I would typically start with a "small solution" (both in one class) and refactor as soon as the class starts getting "too large".

Related Topic