Let's look at two ways of implementing a basic strategy pattern for a Calculator class that performs a calculation implemented as a strategy. I'll attempt to make the surface areas of each class the same so we can look at the different requirements needed to implement the pattern through traditional OOP and then by using delegates.
To start, we will be using a common enumerator to define the different methods our Calculator class can perform:
public enum CaluclationType
{
Add = 0,
Subtract = 1,
Multiply = 2,
Divide = 3
}
When we implement, we want to code that looks like the following where we set the calculation strategy using the previously CaluclationType enum:
Calculator calc = new Calculator(CaluclationType.Add);
Console.WriteLine(calc.GetValue(1.2, 3.4));
calc.CalculationType = CaluclationType.Subtract;
Console.WriteLine(calc.GetValue(1.2, 3.4));
calc.CalculationType = CaluclationType.Multiply;
Console.WriteLine(calc.GetValue(1.2, 3.4));
calc.CalculationType = CaluclationType.Divide;
Console.WriteLine(calc.GetValue(1.2, 3.4));
Part I. OOP Implementation
To get started with the OOP implementation of our solution we'll need an interface that defines the strategy we are implementing:
public interface ICalculate
{
double Calculate(double inputA, double inputB);
}
And then use this interface in all the different calculations we'll be needing:
public class Adder : ICalculate
{
public double Calculate(double inputA, double inputB)
{
return inputA + inputB;
}
}
public class Subtractor : ICalculate
{
public double Calculate(double inputA, double inputB)
{
return inputA - inputB;
}
}
public class Multiplier : ICalculate
{
public double Calculate(double inputA, double inputB)
{
return inputA * inputB;
}
}
public class Divider : ICalculate
{
public double Calculate(double inputA, double inputB)
{
return inputA / inputB;
}
}
Next, we'll define our OopCalculator class that executes the strategy. The constructor is going to be passed the CaluclationType enum. We need to keep member variables for the strategy and the enumerator and we'll have a helper method to instantiate the correct class implementing our ICalculate strategy.
public class OopCalculator
{
public OopCalculator (CaluclationType type)
{
m_calculator = GetCalculation(type);
}
private ICalculate m_calculator;
private CaluclationType m_calculationType;
public CaluclationType CalculationType
{
get { return m_calculationType; }
set
{
m_calculationType = value;
m_calculator = GetCalculation(value);
}
}
private ICalculate GetCalculation(CaluclationType type)
{
switch (type)
{
case CaluclationType.Add: return new Adder();
case CaluclationType.Subtract:return new Subtractor();
case CaluclationType.Multiply: return new Multiplier();
case CaluclationType.Divide: return new Divider();
default: throw new ArgumentException("Unexpected CalculationType");
}
}
public double GetValue(double inputA, double inputB)
{
return m_calculator.Calculate(inputA, inputB);
}
}
Ok, so in the end we are left with one main class, one interface (which could have been an abstract base class as well), four strategy classes and our main class all in approximately 100 lines of code.
Part II. Delegate implementation
In our delegate implementation, we'll be using a delegate in place of the strategy interface so let's first define a really generic delegates as follows:
public delegate TOutput Calculate<TOutput, TInput, TInput2>(TInput input1, TInput2 input2);
And the only other thing we'll be needing is the implementing class. It will have the same constructor as our OopCalculator and be implemented exactly the same. We'll have a switch statement just like in the OOPCalculator to get the correct strategy. The main difference is that all the methods containing the calculations will be internal to the class which we'll be wiring up with delegates:
public class Calculator
{
public Calculator(CaluclationType cType)
{
m_calculationType = cType;
m_calculator = GetCalculation(cType);
}
private CaluclationType m_calculationType;
private Calculate<double, double, double> m_calculator;
public CaluclationType CalculationType
{
get { return m_calculationType; }
set
{
m_calculationType = value;
m_calculator = GetCalculation(value);
}
}
public double GetValue(double inputA, double inputB)
{
return m_calculator(inputA, inputB);
}
private Calculate<double, double, double> GetCalculation(CaluclationType value)
{
switch (value)
{
case CaluclationType.Add: return AddMethod;
case CaluclationType.Subtract: return SubtractMethod;
case CaluclationType.Multiply: return MultiplyMethod;
case CaluclationType.Divide: return DivideMethod;
default: throw new ArgumentException("Unexpected CalculationType");
}
}
#region Methods
private static double AddMethod(double input1, double input2)
{
return input1 + input2;
}
private static double SubtractMethod(double input1, double input2)
{
return input1 - input2;
}
private static double MultiplyMethod(double input1, double input2)
{
return input1 * input2;
}
private static double DivideMethod(double input1, double input2)
{
return input1 / input2;
}
#endregion
}
So in the end using the delegate approach we are left with one main class, one delegate (which can be reused because it's so generic) and around 60 lines of code. That's significantly less code, classes and resulting complexity to keep track of because we no longer have to have an individual object representing each strategy.
Hopefully this gives you some insight as to the power and usefulness of delegates.
Until next time,
Happy coding
Mukesh KumarPosted Oct 19, 2015, 1:11 AM
Good Job
Bathiya LakmalPosted Feb 11, 2009, 5:28 AM
Huy, I wanted to know whether, when executing inside a delegate (i.e. inside the method that has been invoked by the delegate method), does it block C# events from being fetched by the program, until the delegate completes it's execution..?
BarteditedPosted Jul 12, 2007, 11:56 AMEdited Jul 12, 2007, 12:01 PM
I noticed in your delegate implementation in the constructor you use: m_calculationType = cType; m_calculator = GetCalculation(cType); If you use: CalculationType = cType; You achieve the same thing with the added benefit that if you change the setter for CalculationType you don't have to remember to change your constructor as well. I often run into this same situation and don't know whether or not to access the property or always use the private field of that property when in a constructor. Using the property itself (primarily for ease of maintainability) just feels "wrong" since you access the property of an object during the object's construction. But at times when the setter does several manipulations it also feels wrong to duplicate these line for line in the constructor. Additionally one must typically remember to duplicate any changes to the setter back to the constructor. What are your thoughts?
EricPosted Jun 18, 2007, 1:37 PM
I am not a very good programmer so I tend to get easily confused when the code gets only a bit complicated. So, I don't see that a delegate object was created by using "new" in this example? Shouldn't the delegate object be instantiated with the name of method to be passed as an argument? I tried to find the delegate object here and I couldn't find it :D
MEZIL MatthieuPosted Jun 16, 2007, 8:57 AM
With C# 3.0, you can use the keyword Func. With it, you have not to define your calculate delegate.