Click here to Skip to main content
Click here to Skip to main content

Understanding and Implementing the Strategy Pattern in C#

By , 19 Mar 2012
Rate this:
Please Sign up or sign in to vote.

Introduction

The aim of this article is to understand the basics of the Strategy pattern and try to see when it can be useful and have a rudimentary implementation for better understanding.

Background

There are many scenarios in application development where there are multiple ways of doing the same operation. We want our application to have the possibility of using all these ways of performing the operations. An example could be payment on e-commerce portals, I could choose to pay using net-banking, I could choose to use a credit card, or I can even choose PayPal for making the payment. All these are ways of performing the same operations. Though every choice will have to follow a different application logic, i.e., separate code.

One more example where we could have the same operation in multiple ways is sorting. I could sort a sequence using any one of the sorting algorithms. So when I have a situation when I want to develop the application in such a way that for any operation the user could choose one of many available options, then perhaps that is the right place to incorporate the Strategy pattern.

The philosophy of the Strategy pattern is to have multiple strategies for doing some operation and let the user choose (or some algorithm based on input data) the appropriate strategy to carry out the operation. GoF defines the Strategy pattern as "Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it."

Let us try to understand each component of this class diagram.

  • Strategy: This is the interface common to all algorithms. Context uses this interface to perform the operations.
  • ConcreteStrategy: This is the class that implements the actual algorithm.
  • Context: This is the client application that performs the decision making for which strategy should be used and uses the Strategy interface (which is referring to a ConcreteStrategy object) to perform the operations.

Using the code

So to understand these concepts, let us work on a toy application for converting audio files into different formats (like MP3 to WMA). Here the user will have an option of selecting the source file and then deciding the target output type format. The operation that will be performed is to convert the source file to the target output format.

The multiple strategies that we will be using are to define the quality of the output. The user could choose to convert to a low quality output (perhaps because it is fast and is of small size). The user could choose to convert to a high quality output (perhaps because he wants to use it on high-end audio products from BOSE or B&O), or he could just choose an average quality output (perhaps to put it on his cell phone). We implement all these as separate strategies so that the client code can be independent of the implementation details of these different strategies and will work in the same fashion for any selected strategy.

Note: There is no real application, only a dummy application but perhaps the real application can be developed on the same lines. Also, the choice of the application and design is purely to demonstrate the Strategy pattern in action (could be a bad design if we take the holistic view).

So let us first write the interface Strategy that will be implemented by all ConcreteStrategys.

interface IStrategy
{
    void Convert();
} 

Once we have the interface ready, we can write the ConcreteStrategy classes.

//Strategy class for low quality conversion
class LowQualityConversion : IStrategy
{
    const int QUALITY_VALUE = 50;

    #region IStrategy Members
    public void Convert()
    {
        FileConvertor convertor = new FileConvertor(QUALITY_VALUE);
        convertor.ConvertFile();
    }
    #endregion
}

//Strategy class for average quality conversion
class AverageQualityConversion :IStrategy
{
    const int QUALITY_VALUE = 150;

    #region IStrategy Members
    public void Convert()
    {
        FileConvertor convertor = new FileConvertor(QUALITY_VALUE);
        convertor.ConvertFile();
    }
    #endregion
}

//Strategy class for high quality conversion
class HighQualityConversion : IStrategy
{
    const int QUALITY_VALUE = 250;

    #region IStrategy Members
    public void Convert()
    {
        FileConvertor convertor = new FileConvertor(QUALITY_VALUE);
        convertor.ConvertFile();
    }
    #endregion
} 

We have our concrete classes using another class for doing the actual conversion. This is to demonstrate that the concrete strategy classes should only contain the logic that is different among all strategies and having multiple strategies does not mean that we can have duplication of code.

Note: If we would be writing the Sort example, we will not be needing any helper class since all the sort algorithms and different logic could be simply implemented inside the concrete strategy classes without any code duplication.

Let us see what this class is doing:

class FileConvertor
{
    int qualityParameter;

    public FileConvertor(int qualityParam)
    {
        qualityParameter = qualityParam;
    }        

    public void ConvertFile()
    {
        if (qualityParameter < 100)
        {
            Console.WriteLine("Converting the file in LOW quality");
        }
        else if (qualityParameter > 100 && qualityParameter < 200)
        {
            Console.WriteLine("Converting the file in MEDIUM quality");
        }
        else
        {
            Console.WriteLine("Converting the file in HIGH quality");
        }
    }
} 

and finally let us write the Context class that will be using the IStrategy interface and will have the decision making logic to use the actual strategy.

static void Main(string[] args)
{
    IStrategy selectedStrategy = null;

    Console.WriteLine("Assuming the file for conversion has been selected already");
    
    //Let us try to emulate the selection of quality here
    Console.WriteLine("Enter the type of output \n1. Low Quality\n2. Average Quality\n3. High Quality");
                
    int choice = Console.Read();

    //Now based on the users' choice lets go ahead and select strategy to convert the file
    if (choice == '1')
    {
        selectedStrategy = new LowQualityConversion();
    }
    else if (choice == '2')
    {
        selectedStrategy = new AverageQualityConversion();
    }
    else if (choice == '3')
    {
        selectedStrategy = new HighQualityConversion();
    }

    //Now the code which is doing the conversion. this code beed 
    //not be changes even if we implement more strategies
    if (selectedStrategy != null)
    {
        selectedStrategy.Convert();
    }
}<span style="color: rgb(17, 17, 17); font-family: 'Segoe UI', Arial, sans-serif; font-size: 14px;"> </span>

The file conversion will happen based on the user selected options and the right strategy will be used. Now let us try to compare our code with the GoF class diagram.

strategy C# Image

Points of interest

This pattern can sometimes be used to have a "plug and play" type of architecture where we can have various strategies plugged in to perform any operation without affecting the client code.

History

  • 14 March 2012: First version. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Rahul Rajat Singh
Software Developer (Senior)
India India
I Started my Programming career with C++. Later got a chance to develop Windows Form applications using C#. Currently using C#, ASP.NET & ASP.NET MVC to create Information Systems, e-commerce/e-governance Portals and Data driven websites.

My interests involves Programming, Website development and Learning/Teaching subjects related to Computer Science/Information Systems. IMO, C# is the best programming language and I love working with C# and other Microsoft Technologies.
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
GeneralMy vote of 1 PinmemberEmre Ataseven11-Mar-14 20:27 
Question[My vote of 1] not great PinprofessionalBillW332-Dec-13 6:19 
GeneralMy vote of 1 Pinmembercpsglauco13-Nov-13 2:04 
GeneralMy vote of 3 PinmemberJaikrishan20-Mar-13 23:22 
GeneralMy vote of 5 Pinmembersraoh19-Mar-13 8:29 
Questionconfused with with Factory pattern vs strategy pattern PinmemberAnjum.Rizwi8-Jan-13 1:10 
QuestionPattern suggestion PinmvpJani Giannoudis13-Apr-12 0:17 
You can enhance your strategy pattern using a base classes like this example (C#):
public abstract class Convertor : IStrategy
{
  public void Convert()
  {
    OnConvert();
  } // Convert

  protected abstract void OnConvert();
} // class Convertor
 
or a more specialized base class like this:
public abstract class Convertor : IStrategy
{
  public void Convert()
  {
    OnConvert();
  } // Convert

  protected abstract int Quality { get; }
 
  protected virtual void OnConvert()
  {
    FileConvertor convertor = new FileConvertor( Quality );
    convertor.ConvertFile();
  } // OnConvert

} // class Convertor
 
or even:
public abstract class Convertor : IStrategy
{
  protected Convertor( int quality )
  {
    fileConvertor = new FileConvertor( quality );
  } // Convertor

  public void Convert()
  {
    OnConvert( fileConvertor );
  } // Convert

  protected virtual void OnConvert( FileConvertor convertor )
  {
    convertor.ConvertFile();
  } // OnConvert

  // members
  private readonly FileConvertor fileConvertor;
 
} // class Convertor
 
By using a base class, you have a single entry point for the function, which can be useful for future modifications.
Cheers,
Jani Giannoudis
Meerazo.com - Resource Sharing Made Easy | Co-founder

GeneralMy vote of 3 PinmemberKeith.Badeau22-Mar-12 8:34 
GeneralMy vote of 1 Pinmembersucram20-Mar-12 2:32 
GeneralRe: My vote of 1 PinmemberRahul Rajat Singh20-Mar-12 2:59 
GeneralMy vote of 1 Pinmemberzennie19-Mar-12 21:46 
GeneralMy vote of 1 PinmemberFerretallicA15-Mar-12 15:02 
Questionvery nice Pinmemberadeelsansari15-Mar-12 11:09 
GeneralMy vote of 2 PinmemberVikram.....15-Mar-12 2:09 
GeneralMy vote of 1 PinmemberSelvin15-Mar-12 1:34 
GeneralMy vote of 5 [modified] PinmemberMahmud Hasan14-Mar-12 16:07 
GeneralRe: My vote of 5 Pinmemberzennie19-Mar-12 21:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 20 Mar 2012
Article Copyright 2012 by Rahul Rajat Singh
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid