
Стратегия на C++
Стратегия — это поведенческий паттерн, выносит набор алгоритмов в собственные классы и делает их взаимозаменимыми.
Другие объекты содержат ссылку на объект-стратегию и делегируют ей работу. Программа может подменить этот объект другим, если требуется иной способ решения задачи.
Сложность:
Популярность:
Применимость: Стратегия часто используется в C++ коде, особенно там, где нужно подменять алгоритм во время выполнения программы. Многие примеры стратегии можно заменить простыми lambda-выражениями.
Признаки применения паттерна: Класс делегирует выполнение вложенному объекту абстрактного типа или интерфейса.
Концептуальный пример
Этот пример показывает структуру паттерна Стратегия, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
main.cc: Пример структуры паттерна
/** * Интерфейс Стратегии объявляет операции, общие для всех поддерживаемых версий * некоторого алгоритма. * * Контекст использует этот интерфейс для вызова алгоритма, определённого * Конкретными Стратегиями. */ class Strategy { public: virtual ~Strategy() = default; virtual std::string doAlgorithm(std::string_view data) const = 0; }; /** * Контекст определяет интерфейс, представляющий интерес для клиентов. */ class Context { /** * @var Strategy Контекст хранит ссылку на один из объектов Стратегии. * Контекст не знает конкретного класса стратегии. Он должен работать со * всеми стратегиями через интерфейс Стратегии. */ private: std::unique_ptr<Strategy> strategy_; /** * Обычно Контекст принимает стратегию через конструктор, а также * предоставляет сеттер для её изменения во время выполнения. */ public: explicit Context(std::unique_ptr<Strategy> &&strategy = {}) : strategy_(std::move(strategy)) { } /** * Обычно Контекст позволяет заменить объект Стратегии во время выполнения. */ void set_strategy(std::unique_ptr<Strategy> &&strategy) { strategy_ = std::move(strategy); } /** * Вместо того, чтобы самостоятельно реализовывать множественные версии * алгоритма, Контекст делегирует некоторую работу объекту Стратегии. */ void doSomeBusinessLogic() const { if (strategy_) { std::cout << "Context: Sorting data using the strategy (not sure how it'll do it)\n"; std::string result = strategy_->doAlgorithm("aecbd"); std::cout << result << "\n"; } else { std::cout << "Context: Strategy isn't set\n"; } } }; /** * Конкретные Стратегии реализуют алгоритм, следуя базовому интерфейсу * Стратегии. Этот интерфейс делает их взаимозаменяемыми в Контексте. */ 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 { 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; } }; /** * Клиентский код выбирает конкретную стратегию и передаёт её в контекст. Клиент * должен знать о различиях между стратегиями, чтобы сделать правильный выбор. */ void clientCode() { Context context(std::make_unique<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(std::make_unique<ConcreteStrategyB>()); context.doSomeBusinessLogic(); } 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