Strategy patterns using delegates

Interfaces are the classical way of implementing the strategy pattern, but with .NET we get another similar approach to technically accomplish the same thing. That is by utilizing delegates as part of our parameters list and C# anonymous delegates as our vessel for the strategy.

The first ever discussion I had with Erik Dörnenburg  (http://www.doernenburg.com/) was about delegates, he categorized them as a template interface with one method. While this is not true to a full extent, since they are more complex than that, it is a nice way to look at them in the context of strategy.

Changing the implementation
Instead of creating your own interface and a specific implementation of that interface, you can let the delegate plumbing do it for you. Continuing from the previous example, we could use delegates as the common denominator and act as an interface for the strategies we want. To do this, we just need to create a delegate with the appropriate signature, similar to the signature of the one single method the interface we created in the previous example had.

public delegate bool StrategyDelegate(Consultant consultant);

Since this delegate will act as our interface, we need to change the parameter list to accept an instance of that delegate instead of the something implementing the IStrategy interface. This will allow us to pass any method with the same signature as the delegate as an in parameter to the method.

Consultant FindFirstSuitable(List consultants, StrategyDelegate strategy)

And finally we need to change the code in the foreach loop slightly:

foreach (Consultant consultant in consultants)  {
                if (strategy.Invoke(consultant))
                    return consultant;
            }

This accomplishes the exact same functionality as we did using interfaces. Although the intent is slightly less clear inside the method for this approach (Invoke don not tell me that much about what it actually checks), I will be able to utilize this function without creating new classes and objects.

Also; with C# 2.0 and the ability to create delegates on the fly, so called anonymous delegates, I will be able to be very precise and clear about the intent when I call this method. For example if I wanted to find a cheap consultant I could write:

Consultant consultant = FindFirstSuitable(consultants, 
                                          delegate(Consultant consultant)
                                          { return consultant.HourRate < 20;
                                          );

When I read this method call; it is quite obvious what my intent is and what the rule passed in actually stipulates. I do not have to look for details in external methods or type definitions.

Looking ahead and into the next version of C# (3.0 or whatever number it will get) this will be even neater with some Lambda expressions from the LINQ package. With Lambda you can express an anonymous delegate like this:

 Consultant consultant = FindFirstSuitable(consultants, consultant => consultant.HourRate <20);

Now that is just straight up beautiful!

Strategy pattern usage in the .Net Framework
This usage of the strategy pattern can be found in many places of the Framework but the most evident is in the List<> class. I have written a sum-up earlier on how that is used so I will not go into details but if you are interested you can read about it here:  http://blog.lowendahl.net/?page_id=96

Conclusions
When do we use interfaces and when do we use delegates? There is no clear rule but some guidelines. First of all delegates are much harder to reuse, especially if you use anonymous delegates since they are impossible to reuse, but they are very clear and concise in their intent. Interfaces on the other hand is a bit more complex to implement but will enable you to reuse the strategies across your domain and will let you have more than one method that might be used as part of the decision making. Personally I think that of the two solutions we can use today, interfaces are the more beautiful one (but that will probably change when Lambda is implemented).

Other design pattern examples >

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: