Паттерн «Стратегия» на C++

Приветствую читателей.В этом посте хотел бы показать две реализации паттерна «Стратегия». Один способ на основе наследования, другой на основе шаблонного класса. Итак приступим.Сначала разберемся, что же такое паттерн «Стратегия»? К этому обратимся в википедию и вот что она говорит: Стратегия — поведенческий шаблон проектирования, предназначенный для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости. Это позволяет выбирать алгоритм путем определения соответствующего класса. Шаблон Strategy позволяет менять выбранный алгоритм независимо от объектов-клиентов, которые его используют.

Так выглядит схема паттерна:

b1e052879ada4c38a4c3d027d9f51002.PNGНекоторые из преимуществ паттерна:

Позволяет выбирать алгоритм динамически Вызов всех алгоритмов одним стандартным образом Упрощает процесс добавления новых стратегий (алгоритмов) Избавляет от множественного использования переключателей (if, else) Мотивы использования:

Программа должна обеспечивать различные варианты алгоритма или поведения Нужно изменять поведение каждого экземпляра класса Необходимо изменять поведение объектов на стадии выполнения Так, думаю разобрались для чего нужен этот паттерн. Теперь разберемся как же его реализовать.

Стратегия может быть просто интерфейсом — набором функций объеденным в структуру. С помощью набора стратегий мы создаем общий алгоритм поведения. Стратегия должна иметь общий интерфейс, т. е. должна иметь одинаковый набор функций с общим назначением и разной реализацией.Теперь представим, что мы делаем класс сжатия файлов, этот класс должен уметь сжимать 2-мя способами, сжимать в форматах zip и rar.

Определим иерархию классов:

Базовый класс для стратегий.

struct Compression { virtual void Compress (const string &file) = 0; virtual ~Compression () {} }; Стратегия для сжатия в zip.

struct ZipCompression: Compression { void Compress (const string &file) { … } }; Стратегия для сжатия в rar.

struct RarCompression: Compression { void Compress (const string &file) { … } }; Класс для использования.

class Compressor { private: Compression *ptr;

public: Compressor (Compression *comp) : ptr (comp) { }

void compress (const string &file) { ptr→Compress (file); }

~Compressor () { delete ptr; } }; Использование:

Compressor zip (new ZipCompression); zip.compress («filename»);

Compressor rar (new RarCompression); rar.compress («filename»); Как видим из примера, мы используем одну реализацию класса Compressor, но в конструкторе передали указатели на разные стратегии, таким образом вызывая у класса разное поведение.Следующая реализация основывается на шаблонах C++.

Шаблонная реализация паттерна Как по мне предыдущая реализация выглядит не очень изящно. Следующий способ реализации выглядит короче и красивее. Как и раньше создадим стратегии-алгоритмы: Стратегия для сжатия в zip.

struct ZipCompression { void Compress (const string &file) { … } }; Стратегия для сжатия в rar.

struct RarCompression { void Compress (const string &file) { … } }; Теперь добавим шаблонный класс для использования

template class Compressor { private: CompressionPolicy strategy; public: void Compress (const string &file) { strategy.Compress (file); } }; Использование

typedef Compressor Zip; typedef Compressor Rar; … Этот способ выглядит более аккуратно и избавляет от одного лишнего класса. На самом деле это довольно простой пример, можно было бы комбинировать стратегии, добавить дополнительный тип в шаблон и т. д., но думаю основная идея ясна.Спасибо за внимание.

© Habrahabr.ru