Strategy
With strategy design pattern, it is possible do define a family of algorithms and choose the appropriate one to use at runtime. Since the algorithm is implemented in a separate class, it makes them interchangeable.
Strategy design pattern uses composition approach.
It separates the business logic of a class from the implementation details of algorithms (SRP).
Adding a new algorithm won’t affect the client code (Open/Close Principle).
Structure

Implementation
Classic Example (Dynamic)
// Define the interface common to all supported versions of some algorithms
class Strategy {
public:
virtual ~Strategy() = default;
virtual std::string doAlgorithm(std::string_view data) const = 0;
}
class ConcreteStrategyA : public Strategy {
public:
std::string doAlgorithm(std::string_view data) const override
{
std::string result(data);
std::sort(std::begin(result), std::end(result));
return result;
}
}
class ConcreteStrategyB : public Strategy {
public:
std::string doAlgorithm(std::string_view data) const override
{
std::string result(data);
std::sort(std::begin(result), std::end(result), std::greater<>());
return result;
}
}
class Context
{
private:
std::unique_ptr<Strategy> m_strategy;
public:
explicit Context(std::unique_ptr<Strategy>&& strategy = {}): strategy_(std::move(strategy)) {}
// ALternatively use factory pattern
void set_strategy(std::unique_ptr<Strategy>&& strategy) {
m_strategy = std::move(strategy);
}
void doSomething() const {
if (m_strategy) {
std::string result = m_strategy->doAlgorithm("abcde");
} else {
std::cout << "Context Strategy is not set
";
}
}
}
int main() {
Context context(std::make_unique<ConcreteStrategyA>());
context.doSomething();
}
Static Strategy (Template)
If you want to avoid using vtable.
template<typename LS>
struct TextProcessor {
void append_list(const vector<string> &items) {
m_list_strategy.start(m_oss);
for (auto & item: items)
m_list_strategy.add_list_item(m_oss, item);
m_list_strategy.end(m_oss);
}
string str() const { return m_oss.str(); }
private:
ostringstream m_oss;
LS m_list_strategy;
};
int main() {
// markdown
TextProcessor<MarkdownListStrategy> tp1;
tp1.append_list({ "foo", "bar", "baz" });
cout << tp1.str() << endl;
// html
TextProcessor<HtmlListStrategy> tp2;
tp2.append_list({ "foo", "bar", "baz" });
cout << tp2.str() << endl;
return EXIT_SUCCESS;
}
Functional approach using Lambda
The strategy can be implemented as an anonymous function instead. With this approach, we can avoid bloating the code with extra classes and interfaces
Credits
https://refactoring.guru/design-patterns/strategy
https://vishalchovatiya.com/posts//strategy-design-pattern-in-modern-cpp/