Паттерн Стратегия на C++
Стратегия – это поведенческий паттерн, выносит набор алгоритмов в собственные классы и делает их взаимозаменимыми.
Другие объекты содержат ссылку на объект-стратегию и делегируют ей работу. Программа может подменить этот объект другим, если требуется иной способ решения задачи.
Особенности паттерна на C++
Сложность:
Популярность:
Применимость: Стратегия часто используется в C++ коде, особенно там, где нужно подменять алгоритм во время выполнения программы. Многие примеры стратегии можно заменить простыми lambda-выражениями.
Признаки применения паттерна: Класс делегирует выполнение вложенному объекту абстрактного типа или интерфейса.
Концептуальный пример
Этот пример показывает структуру паттерна Стратегия, а именно – из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
main.cc: Пример структуры паттерна
/**
* Интерфейс Стратегии объявляет операции, общие для всех поддерживаемых версий
* некоторого алгоритма.
*
* Контекст использует этот интерфейс для вызова алгоритма, определённого
* Конкретными Стратегиями.
*/
class Strategy
{
public:
virtual ~Strategy() {}
virtual std::string DoAlgorithm(const std::vector<std::string> &data) const = 0;
};
/**
* Контекст определяет интерфейс, представляющий интерес для клиентов.
*/
class Context
{
/**
* @var Strategy Контекст хранит ссылку на один из объектов Стратегии.
* Контекст не знает конкретного класса стратегии. Он должен работать со
* всеми стратегиями через интерфейс Стратегии.
*/
private:
Strategy *strategy_;
/**
* Обычно Контекст принимает стратегию через конструктор, а также
* предоставляет сеттер для её изменения во время выполнения.
*/
public:
Context(Strategy *strategy = nullptr) : strategy_(strategy)
{
}
~Context()
{
delete this->strategy_;
}
/**
* Обычно Контекст позволяет заменить объект Стратегии во время выполнения.
*/
void set_strategy(Strategy *strategy)
{
delete this->strategy_;
this->strategy_ = strategy;
}
/**
* Вместо того, чтобы самостоятельно реализовывать множественные версии
* алгоритма, Контекст делегирует некоторую работу объекту Стратегии.
*/
void DoSomeBusinessLogic() const
{
// ...
std::cout << "Context: Sorting data using the strategy (not sure how it'll do it)\n";
std::string result = this->strategy_->DoAlgorithm(std::vector<std::string>{"a", "e", "c", "b", "d"});
std::cout << result << "\n";
// ...
}
};
/**
* Конкретные Стратегии реализуют алгоритм, следуя базовому интерфейсу
* Стратегии. Этот интерфейс делает их взаимозаменяемыми в Контексте.
*/
class ConcreteStrategyA : public Strategy
{
public:
std::string DoAlgorithm(const std::vector<std::string> &data) const override
{
std::string result;
std::for_each(std::begin(data), std::end(data), [&result](const std::string &letter) {
result += letter;
});
std::sort(std::begin(result), std::end(result));
return result;
}
};
class ConcreteStrategyB : public Strategy
{
std::string DoAlgorithm(const std::vector<std::string> &data) const override
{
std::string result;
std::for_each(std::begin(data), std::end(data), [&result](const std::string &letter) {
result += letter;
});
std::sort(std::begin(result), std::end(result));
for (int i = 0; i < result.size() / 2; i++)
{
std::swap(result[i], result[result.size() - i - 1]);
}
return result;
}
};
/**
* Клиентский код выбирает конкретную стратегию и передаёт её в контекст. Клиент
* должен знать о различиях между стратегиями, чтобы сделать правильный выбор.
*/
void ClientCode()
{
Context *context = new Context(new ConcreteStrategyA);
std::cout << "Client: Strategy is set to normal sorting.\n";
context->DoSomeBusinessLogic();
std::cout << "\n";
std::cout << "Client: Strategy is set to reverse sorting.\n";
context->set_strategy(new ConcreteStrategyB);
context->DoSomeBusinessLogic();
delete context;
}
int main()
{
ClientCode();
return 0;
}
Output.txt: Результат выполнения
Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
abcde
Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
edcba