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

в 17:38, , рубрики: c++, ооп, паттерн стратегия, паттерны, Программирование

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

Стратегия — поведенческий шаблон проектирования, предназначенный для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости. Это позволяет выбирать алгоритм путем определения соответствующего класса. Шаблон Strategy позволяет менять выбранный алгоритм независимо от объектов-клиентов, которые его используют.

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

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

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

  • Позволяет выбирать алгоритм динамически
  • Вызов всех алгоритмов одним стандартным образом
  • Упрощает процесс добавления новых стратегий(алгоритмов)
  • Избавляет от множественного использования переключателей (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 CompressionPolicy>
class Compressor
{
private:
    CompressionPolicy strategy;
    
public:
    void Compress(const string &file)
    {
        strategy.Compress(file);
    }
};

Использование

typedef Compressor<ZipCompression> Zip;
typedef Compressor<RarCompression> Rar;
...

Этот способ выглядит более аккуратно и избавляет от одного лишнего класса, а также повышает производительность за счет отсутствия виртуальных функций. На самом деле это довольно простой пример, можно было бы комбинировать стратегии, добавить дополнительный тип в шаблон и т. д. но думаю основная идея ясна.
Спасибо за внимание.

Автор: Vladislav_Dudnikov

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js