Skip to main content

C# Strategy Pattern - using Interfaces and Delegates [Beginner]


Have you encountered code with a lot of if-else logic? I am sure you have. Most of the time we see that kind of logic, behind the scenes, it's a strategy problem. Someone trying to figure out a strategy to solve the problem using the most primitive language tool - 'if' statement. 

However, when it comes to Object Oriented Programming, it's important to understand that the code needs to be reusable, testable and at the same time scalable. That's where a Strategy Pattern comes in handy. Let's take a look at an example. (Examples are in C# but the concept can be applied to any Object-Oriented Language)

First, we will see the code for something that is written in the old fashion.
public int Calculate(int x, int y)
{
     if(action == "Add")
       {
          return x+y;}
       }
     else if(action == "Sub")
       {
         return x-y;
       }
    else if
      ......
      .... and so on.
}
 
So, problems with this approach:
  1. If you were to add another Strategy (let's say Divide), you will have to change the code in the class. That means you will have to modify the class. However, that is not a good practice. It violates the principle of OCP or Open Closed Principle. OCP says that the classes are Open for Extension but NOT for Modification.
  2. Too many 'if' blocks down the line.
  3. Hard-coded strings. I am sure no one likes those. Counter: We can use Enums, yes but if we can do without using Enums, why add another dependency.
  4. Removing a strategy becomes hard, too.

So, now let's focus on a good way to do it. Or use the Strategy Pattern - a pattern that let's you choose the strategy on the fly. The strategy depends on the type of the object. The principle behind this pattern is - "Program to an Interface and not to an Implementation". That makes the solution easy to test, extensible and maintainable. 

We start by defining the Interface.
  public interface ICalculate
    {
        public int Calculate(int x, int y);
    }
 
Now we can implement this interface.
public class Add : ICalculate
    {

        #region ICalculate Members

        public int Calculate(int x, int y)
        {
            return x+ y;
        }

        #endregion
    }

   public class Subtract : ICalculate
    {

        #region ICalculate Members

        public int Calculate(int x, int y)
        {
            return x* y;
        }

        #endregion
    }
 
Now, when we use this code, this is how we call the code. ICalculate calculate = new Add(); int sum = calculate.Calculate(a , b);

Since the object is of type Add, we don't have to check for the type(as we did above) to call the appropriate function. So, at run time, the Calculate method from the class 'Add' gets called. 

Here are some observations: 1. How about adding a new Strategy. It's as simple as adding another class, for example, Multiply. We just need to make sure that it implements the interface ICalculate.
 public class Multiply : ICalculate
    {

        #region ICalculate Members

        public int Calculate(int num1, int num2)
        {
            return num1 * num2;
        }

        #endregion
    }
 
  1. This does not violate the OCP. In fact, it upholds it.
  2. If we need to remove a strategy, we can remove that class from our code and it won't affect any other code (like it would have in the legacy code- we would have to remove that if-else statement).
  3. It has no hard-coded strings.

Bonus: How about coding Strategy Pattern using delegates?
It's very simple.
 public class Calculator
    {
       public static int Add(int x, int y)
       {
           return x+ y;
       }

       public static int Subtract(int x, int y)
       {
           return x- y;
       }

       public static int Multiply(int x, int y)
       {
           return x* y;
       }

       public static int Divide(int x, int y)
       {
           return x/ y;
       }

    }
 
Now, we can use Delegates to call this code.
Func<int, int, int> calculate = Calculator.Add;
int sum = calculate(100, 50);
or
calculate = Calculator.Subtract;
int difference = calculate(100, 50);
 
Is this too many lines of code. Well, in that case, we can just code the method inline. Now, we don't' need the class Calculator.
Func<int, int, int> calculate;
calculate = (x, s) => x + s;
 
Delegates vs Interfaces: Whereas delegates can reduce the code base and increase readability of code, you have to be careful on how you use it otherwise you might end up sacrificing testability.

Comments

Popular posts from this blog

C# Snippet - Shuffling a Dictionary [Beginner]

Randomizing something can be a daunting task, especially with all the algorithms out there. However, sometimes you just need to shuffle things up, in a simple, yet effective manner. Today we are going to take a quick look at an easy and simple way to randomize a dictionary, which is most likely something that you may be using in a complex application. The tricky thing about ordering dictionaries is that...well they are not ordered to begin with. Typically they are a chaotic collection of key/value pairs. There is no first element or last element, just elements. This is why it is a little tricky to randomize them. Before we get started, we need to build a quick dictionary. For this tutorial, we will be doing an extremely simple string/int dictionary, but rest assured the steps we take can be used for any kind of dictionary you can come up with, no matter what object types you use. Dictionary < String , int > origin = new Dictionary < string , int >();

C# Snippet - The Many Uses Of The Using Keyword [Beginner]

What is the first thing that pops into your mind when you think of the using keyword for C#? Probably those lines that always appear at the top of C# code files - the lines that import types from other namespaces into your code. But while that is the most common use of the using keyword, it is not the only one. Today we are going to take a look at the different uses of the using keyword and what they are useful for. The Using Directive There are two main categories of use for the using keyword - as a "Using Directive" and as a "Using Statement". The lines at the top of a C# file are directives, but that is not the only place they can go. They can also go inside of a namespace block, but they have to be before any other elements declared in the namespace (i.e., you can't add a using statement after a class declaration). Namespace Importing This is by far the most common use of the keyword - it is rare that you see a C# file that does not h

C# WPF Printing Part 2 - Pagination [Intermediate]

About two weeks ago, we had a tutorial here at SOTC on the basics of printing in WPF . It covered the standard stuff, like popping the print dialog, and what you needed to do to print visuals (both created in XAML and on the fly). But really, that's barely scratching the surface - any decent printing system in pretty much any application needs to be able to do a lot more than that. So today, we are going to take one more baby step forward into the world of printing - we are going to take a look at pagination. The main class that we will need to do pagination is the DocumentPaginator . I mentioned this class very briefly in the previous tutorial, but only in the context of the printing methods on PrintDialog , PrintVisual (which we focused on last time) and PrintDocument (which we will be focusing on today). This PrintDocument function takes a DocumentPaginator to print - and this is why we need to create one. Unfortunately, making a DocumentPaginator is not as easy as