Using the Strategy Design Pattern in C# ASP .NET

modified

Introduction

Developing software can quickly become a complicated process, with lists of funtions, procedures, loops, and repetitive code. Understanding theories of design patterns and implementing them, when neccessary, can help simplify a software’s design.

Design patterns have been around for many years, but have recently began gaining popularity due, in part, to Microsoft’s promotion of design patterns with the C# .NET framework. If you take a look throughout the .NET framework, you’ll recognize many design patterns already existing. In fact, you use them every day in your C# ASP .NET coding, probably without realizing it.

For those who are new to design patterns, the Strategy pattern can be a very handy tool in your C# ASP .NET software design. To describe how to implement the Strategy design pattern, we’ll start with the typical “Hello World” Stratgey design pattern.

Strategy Design Patterns in C# ASP .NET Web Applications

What’s in a Strategy Design Pattern?

This pattern is essentially just what its name describes. It’s a strategy of dealing with a code design. The main purpose of the Strategy pattern is to decouple concrete code into separate classes, which promotes reusability. As your project grows in size, having reusable modules is a neccessity. This pattern also allows you to easily add new modules into a C# ASP .NET software design with limited impact on the rest of the system. This also makes regression testing much easier.

The Strategy pattern is used to separate different types of algorithms that serve a similar purpose. An example would be sorting an array. There are several types of sort algorithms. By using a Strategy design pattern, each algorithm is separated in its own module and you tell the sort class which algorithm you wish to use. In the future, when you need to add a new algorithm, you simply add a new module.

For example (pseudo-code):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
StrategySort MySort = new StrategySort(new QuickSort());
MySort.Sort(data);

MySort = new StrategySort(new BubbleSort());
MySort.Sort(data);

class QuickSort()
{
   ...
}

class BubbleSort()
{
   ...
}

Of course, the non-pattern method would be to have a single sort class with a series of if-then blocks. You would pass in a variable which indicates which sort to use and the runtime would fall through to the appropriate block. The problem with this method is code clutter, multiple if-thens, and non-reusable code.

For example (pseudo-code):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NonStrategySort MySort = new NonStrategySort();
MySort.Sort(data, "QuickSort");
MySort.Sort(data, "BubbleSort");

void NonStrategySort(void* data, string strAlgorithm)
{
   if (strAlgorithm == "QuickSort")
   {
      ...
   }
   else if (strAlgorithm == "BubbleSort")
   {
      ...
   }
}

Note, the actual power of the design pattern is in the sort method implementation.

The Main Program

Before we get to the details of implementing the Strategy design pattern, let’s start with the main program that uses the pattern. It would look like the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
static void Main(string[] args)
{
    HelloWorldClient MyHelloWorld = new HelloWorldClient(new HelloWorldEnglish());
    MyHelloWorld.SayHello("John Doe");

    MyHelloWorld = new HelloWorldClient(new HelloWorldSpanish());
    MyHelloWorld.SayHello("John Doe");

    MyHelloWorld = new HelloWorldClient(new HelloWorldGerman());
    MyHelloWorld.SayHello("John Doe");

    Console.ReadKey();
}

Notice that we create a single instance of the HelloWorldClient. The beauty of patterns is reusability. In this case, we are reusing a single “client”, which actually uses a C# interface, to call the specific types of HelloWorld that we want to use.

The Strategy Client

It is customary to implement a client for the Strategy pattern. The main code will utilize the client, which in turn calls the appropriate classes for the pattern itself. For our HelloWorld example, the following client was created:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class HelloWorldClient
{
    private IHelloWorldStrategy strategy;

    public HelloWorldClient(IHelloWorldStrategy s)
    {
        strategy = s;
    }

    public void SayHello(string strName)
    {
        strategy.SayHello(strName);
        Console.WriteLine("");
    }
}

The first important part to notice is that we’re using an Interface as the implementation of the strategy. This allows us a generic pointer to whichever algorithm we end up actually using. If you’re familiar with C++, this is also called polymorphism.

Notice the constructor takes a variable of the interface type. This is powerful because, if we derive each algorithm class from the same interface, then we can pass the client algorithm and it will be able to handle any kind. Even new algorithms that we create; as long as they implement the same interface, can be utilized in this client.

In a nutshell, if we have an interface called Box, and we have a series of objects like Square, Rectangle, Cube, rather than referring to them by their actual name, we can use their common denominator - a Box.

The final part of our HelloWorld client is, of course, the SayHello function, which is self explainable.

They All Look the Same to Me

The most important part of the Strategy pattern in C# ASP .NET is the main interface. This is what allows the polymorphism and allows us to pass the client any type of algorithm, as long as it uses the same interface. While this sounds complicated, the interface itself is far from it.

1
2
3
4
interface IHelloWorldStrategy
{
    void SayHello(string strName);
}

That’s really all there is to the interface. We’re just giving a blueprint of what each algorithm will contain at a minimum. That is, each algorithm we plan to use with our HelloWorld client will have a SayHello function.

The Meat of the Strategy Design Pattern

Of course, we can’t finish our pattern without some actual implementation. In the case of the HelloWorld example, we’ll make a few different algorithms to output the “Hello World” phrase in a slightly different way.

1
2
3
4
5
6
7
8
9
10
11
class HelloWorldEnglish : IHelloWorldStrategy
{
    #region IHelloWorldStrategy Members

    public void SayHello(string strName)
    {
        Console.WriteLine("Hello World, " + strName);
    }

    #endregion
}

Notice the class begins by implementing the IHelloWorldStrategy interface that we defined above. This assures that our HelloWorld client will recognize the class and be able to work with it. We fill in the body of the SayHello function by just outputing some text. We may as well add a few more HelloWorld algorithms.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class HelloWorldSpanish : IHelloWorldStrategy
{
    #region IHelloWorldStrategy Members

    public void SayHello(string strName)
    {
        Console.WriteLine("Hola mundo, " + strName);
    }

    #endregion
}

class HelloWorldGerman : IHelloWorldStrategy
{
    #region IHelloWorldStrategy Members

    public void SayHello(string strName)
    {
        Console.WriteLine("Hallo Welt, " + strName);
    }

    #endregion
}

So What’s It Do?

With the algorithms and interface complete, the strategy design pattern is ready to be used. Back in the original main() function, you can see how you would create a single HelloWorldClient and pass it the desired algorithm to run. In this case, you can pick a HelloWorld from a few languages.

This may seem like a lot of code just to output three lines to the C# .NET console, but remember that the key is decoupling the individual algorithms from their implementation and promoting reusability. If your project is relatively small, maybe you could be better off with a single function to implement the algorithms. However, for larger projects or ones that are predicted to grow, using the Strategy pattern can save time in the long-term.

Conclusion

The Strategy design pattern is used in many places in the C# ASP .NET framework, with the sort routine being just one of many. By utilizing the power of this pattern, especially with enterprise software development, you can increase reusability in your software architecture and take full advantage of the power of object oriented programming in C# .NET. If your existing project contains different algorithms based on a similar purpose, you may see the perfect opportunity to increase your projects reusability by using the strategy design pattern with C# ASP .NET.

About the Author

This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.

Share