Skip to content
This repository was archived by the owner on Apr 6, 2025. It is now read-only.

ООП Лекция 12. Порождающие паттерны.

Vladislav Mansurov edited this page May 5, 2022 · 4 revisions

Общие сведения о паттернах

Отличие шаблонов от паттернов: шаблон - конкретная реализация чего-либо, а паттерн - шаблон для решения какой-то задачи (как может решаться данная задача). Паттерн мы всегда адаптируем к своей задаче.

Преимущества:

  • Мы имеем готовое решение
  • Засчет готового решения - нюансы все выявлены => надежный код
  • Повышается скорость разработки
  • Повышается читаемость кода
  • Улучшается взаимодействие с коллегами

Порождающие паттерны

  • Задача: создание объектов
  • Проблема: замещение: при использовании полиморфизма, мы работаем с указателями или ссылками на базовое понятие, возникает проблема с созданием объекта (не может быть полиморфных конструкторов).

image

Фабричный метод

Идея

Разнести на две задачи:

  1. Принятие решения, какой объект создавать.
  2. Создание объекта, причем при создании объекта нужно "отвязаться" от конкретного типа.

Мы будем создавать специальные объекты, которые отвечают за порождение других объектов.

A* p = new B;
// заменить на
A* p = cr->create();

Диаграмма

image

Пример 1 Фабричный метод (Factory Method). Новый объект.

# include <iostream>
# include <memory>

using namespace std;

class Product;

class Creator
{
public:
    virtual unique_ptr<Product> createProduct() = 0;
};

template <typename Tprod>
class ConCreator : public Creator
{
public:
    virtual unique_ptr<Product> createProduct() override
    {
        return unique_ptr<Product>(new Tprod());
    }
};

#pragma region Product
class Product
{
public:
    virtual ~Product() = 0;
    virtual void run() = 0;
};

Product::~Product() {}

class ConProd1 : public Product
{
public:
    virtual ~ConProd1() override { cout << "Destructor;" << endl; }
    virtual void run() override { cout << "Method run;" << endl; }
};

#pragma endregion

int main()
{
    shared_ptr<Creator> cr(new ConCreator<ConProd1>());
    shared_ptr<Product> ptr = cr->createProduct();

    ptr->run();
}

Основная задача этого паттерна - очистить код от оператора new.

Мы принимаем решения о том, какой объект создавать, во время выполнения программы (мы можем в время выполнения программы менять своё решение, во время работы может меняться поведение системы);

Пример 2 Фабричный метод (Factory Method). Без повторного создания.

Один раз создав продукт, в дальнейшем мы его можем только возвращать.

# include <iostream>
# include <memory>

using namespace std;

class Product;

class Creator
{
public:
    shared_ptr<Product> getProduct();

protected:
    virtual shared_ptr<Product> createProduct() = 0;

private:
    shared_ptr<Product> product;
};

template <typename Tprod>
class ConCreator : public Creator
{
protected:
    virtual shared_ptr<Product> createProduct() override
    {
        return shared_ptr<Product>(new Tprod());
    }
};

#pragma region Method Creator
shared_ptr<Product> Creator::getProduct()
{
    if (!product)
    {
        product = createProduct();
    }

    return product;
}

#pragma endregion


#pragma region Product
class Product
{
public:
    virtual ~Product() = 0;
    virtual void run() = 0;
};

Product::~Product() {}

class ConProd1 : public Product
{
public:
    virtual ~ConProd1() override { cout << "Destructor;" << endl; }
    virtual void run() override    { cout << "Method run;" << endl; }
};

#pragma endregion

int main()
{
    shared_ptr<Creator> cr(new  ConCreator<ConProd1>());
    shared_ptr<Product> ptr1 = cr->getProduct();
    shared_ptr<Product> ptr2 = cr->getProduct();

    cout << ptr1.use_count() << endl;
    ptr1->run();
}

Solution

Принятие решения о том, кто будет создавать объект, мы выносим. Класс, принимающий решение, объект не создает. Он создает объект для его создания.

Solution предоставляет метод для регистрации creator-ов.

В одном месте принимаем решение, в другом - создаем объект. Отвязываем код от создания конкретных объектов.

На основе чего он может принять решение? Solution должен быть независим от реализации, от конкретного набора классов.

Идея: мы создаем карту продуктов, которые у нас существуют, при добавлении класса регистрируем новый продукт в этой карте, далее, используя эту карту, осуществляем выбор.

Пример 3 Фабричный метод (Factory Method). Разделение обязанностей.

Solution предоставляет метод для регистрации (в данном случае) Creator'ов. В данном случае для карты - map, состоящий из пар (pair): ключ + значение. Таким образом мы избавились от конструкции switch.

# include <iostream>
# include <memory>
# include <map>

using namespace std;

class Product;

class Creator
{
public:
    virtual unique_ptr<Product> createProduct() = 0;
};

template <typename Tprod>
class ConCreator : public Creator
{
public:
    virtual unique_ptr<Product> createProduct() override
    {
        return unique_ptr<Product>(new Tprod());
    }
}; 

#pragma region Product
class Product
{
public:
    virtual ~Product() = 0;
    virtual void run() = 0;
};

Product::~Product() {}

class ConProd1 : public Product
{
public:
    virtual ~ConProd1() override { cout << "Destructor;" << endl; }
    virtual void run() override { cout << "Method run;" << endl; }
};

#pragma endregion

unique_ptr<Creator> createConCreator()
{
    return unique_ptr<Creator>(new ConCreator<ConProd1>());
}

class Solution
{
public:
    typedef unique_ptr<Creator> (*CreateCreator)();


    bool registration(size_t id, CreateCreator createfun)
    {
        return callbacks.insert(CallBackMap::value_type(id, createfun)).second;
    }
    bool check(size_t id) { return callbacks.erase(id) == 1; }

    unique_ptr<Creator> create(size_t id)
    {
        CallBackMap::const_iterator it = callbacks.find(id);

        if (it == callbacks.end())
        {
            throw IdError();
        }

        return unique_ptr<Creator>((it->second)());
    }

private:
    using CallBackMap = map<size_t, CreateCreator>;

    CallBackMap callbacks;
};

int main()
{
    Solution solution;

    solution.registration(1, createConCreator);

    shared_ptr<Creator> cr(solution.create(1));
    shared_ptr<Product> ptr = cr->createProduct();

    ptr->run();
}
Creator'ы такие же

Абстрактная Фабрика

Clone this wiki locally