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

Understanding and Implementing the Strategy Pattern in C# and C++

By , 19 Mar 2012
 

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 ecommerce portals, I could choose to pay using netbanking, 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();
}

And here is the C++ Implementation of Strategy

class IStrategy
{
public:

	virtual void Convert() = 0;	
};

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
}

Now Let us see the C++ implementation of ConcreteStrategy

//Class for Low Quality Conversion
class LowQualityConversion : public IStrategy
{
	const int QUALITY_VALUE;
public:
	LowQualityConversion()
		: QUALITY_VALUE(50)
	{

	}

	void Convert()
	{
		FileConvertor convertor(QUALITY_VALUE);
		convertor.ConvertFile();
	}	
};

//Class for Average Quality Conversion
class AverageQualityConversion : public IStrategy
{
	const int QUALITY_VALUE;
public:
	AverageQualityConversion()
		: QUALITY_VALUE(150)
	{

	}

	void Convert()
	{
		FileConvertor convertor(QUALITY_VALUE);
		convertor.ConvertFile();
	}
};

//Class for High Quality Conversion
class HighQualityConversion : public IStrategy
{
	const int QUALITY_VALUE ;
public:
	HighQualityConversion()
		: QUALITY_VALUE(250)
	{

	}

	void Convert()
	{
		FileConvertor convertor(QUALITY_VALUE);
		convertor.ConvertFile();
	}
};

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");
        }
    }
}

Let us look at the C++ Implementation of this class too

class FileConvertor
{
	int qualityParam_;
public:

	FileConvertor(int qualityParam)
		: qualityParam_(qualityParam)
	{
	}

	void ConvertFile()
        {
        if (qualityParam_ < 100)
        {
			std::cout << "Converting the file in LOW quality";
        }
        else if (qualityParam_ > 100 && qualityParam_ < 200)
        {
            std::cout << "Converting the file in MEDIUM quality";
        }
        else
        {
            std::cout << "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();
    }
}

and the C++ Implementation for the Context is

int _tmain(int argc, _TCHAR* argv[])
{
	IStrategy *selectedStrategy = 0;

    cout << "Assuming the file for conversion has been selected already";
    
    //Let us try to emulate the selection of quality here
    cout << "Enter the type of output \n1. Low Quality\n2. Average Quality\n3. High Quality";
                
    int choice;
	cin >> choice;

    //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 != 0)
    {
        selectedStrategy->Convert();
		delete selectedStrategy;
    }
	return 0;
};

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.
  • 20 March 2012: Added the C++ Implementation for Strategy Pattern.

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
Member
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.
 
Some CodeProject Achievements:
  • 9th in Best Web Dev article of March 2013
  • 7th in Best Web Dev article of January 2013
  • 2nd in Best C# article of December 2012
  • 5th in Best overall article of December 2012
  • 5th in Best C# article of October 2012
  • 4th in Best Web Dev article of September 2012
  • 3rd in Best C# article of August 2012
  • 5th in Best Web Dev article of August 2012
  • 5th in Best Web Dev article of July 2012
  • 3rd in Best Overall article of June 2012
  • 2nd in Best Web Dev article of June 2012
  • 5th in Best Web Dev article of May 2012
  • 6th in Best Web Dev article of April 2012
  • 4th in Best C++ article of April 2012
  • 5th in Best C++ article of March 2012
  • 7th in Best Web Dev article of March 2012
  • 5th in Best Web Dev article of February 2012
  • 7th in Best Web Dev article of February 2012
  • 9th in Best Web Dev article of February 2012
  • 5th in Best C++ article of February 2012

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 3memberKeith.Badeau22 Mar '12 - 8:34 

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

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