“Strategy Design Pattern” — Supports the ‘O’ of SOLID Principle
The “O” of SOLID Principles stands for “Open for Extension and Closed for Modification”. In Object-Oriented Programming, good code should contain classes that follow the SOLID Principles. So where exactly can we use this design pattern and what is the problem we will be solving?
Let us assume we have some data in a text file and the same data in a binary file. The caller should not be bothered about how to read the data, it should have a method that says getData() and should know the type of file from where the data has to be parsed. A strategy interface solves this problem very easily. And, in later stages, the developer can also extend the interface to read data from an XML file. This shows the code supports both the Open-Close Principle and implements a proper Design Pattern to solve a problem. Another example where Strategy patterns can be used effectively would be if we want to build an application irrespective of the Operating system. We know there are methods/functions which are OS-specific. We can encapsulate them into classes, derive the OS Specific classes from an interface, and write a factory method which the caller can call to run the function without bothering about the inner implementation. Let us try to understand the concept with some pseudo-code.
Interface Class
namespace
{
enum class DataFormatType
{
TextFile,
BinaryFile,
};
class IDataReader
{
public:
virtual const int getData() const= 0;
}
}
Text File Reader Concrete Class
namespace
{
class TextFileReader : public IDataReader
{
public:
TextFileReader(std::ifstream& inputFile) : inputFile(inputFile)
{
}
const int getData() const override
{
//Text File Reader Implementation
}
private:
ifstream& inputFile;
};
}
Binary File Reader Concrete Class
namespace
{
class BinaryFileReader : public IDataReader
{
public:
BinaryFileReader(std::ifstream& inputFile) : inputFile(inputFile)
{
}
const int getData() const override
{
//Binary File Reader Implementation
}
private:
ifstream& inputFile;
};
}
Factory Method to call the concrete classes
namespace
{
std::unique_ptr< IDataReader > createDataReaderFactory( DataFormatType d , std::ifstream& in)
{
switch(d)
{
case DataFormatType::TextFile : return std::make_unique< TextFileReader > (in);
case DataFormatType::BinaryFile : return make_unique < BinaryFileReader > (in);
default: return nullptr;
}
}
}
caller
int main()
{
//can have a text file called TestTextFile.txt containing one integer type data
.....
auto p = createDataReaderFactory( DataFormatType::TextFile, input); // input here is the ifstream object
int val = p->getData(); // We will get the value from the text file here without bothering about the inner details.....
return 0;}
Knowing and implementing this pattern can solve a lot of complex issues, and save the developer from a lot of if/else statements that would be used otherwise.
int main()
{
//can have a text file called TestTextFile.txt containing one integer type data
auto p = createDataReaderFactory( DataFormatType::TextFile, input); // input here is the ifstream object
int val = p->getData(); // We will get the value from the text file here without bothering about the inner details.
}
Please follow, like, comment, share if you like this.