-
Notifications
You must be signed in to change notification settings - Fork 2
ООП Лекция 12. Порождающие паттерны.
Отличие шаблонов от паттернов: шаблон - конкретная реализация чего-либо, а паттерн - шаблон для решения какой-то задачи (как может решаться данная задача). Паттерн мы всегда адаптируем к своей задаче.
Преимущества:
- Мы имеем готовое решение
- Засчет готового решения - нюансы все выявлены => надежный код
- Повышается скорость разработки
- Повышается читаемость кода
- Улучшается взаимодействие с коллегами
- Задача: создание объектов
- Проблема: замещение: при использовании полиморфизма, мы работаем с указателями или ссылками на базовое понятие, возникает проблема с созданием объекта (не может быть полиморфных конструкторов).

Разнести на две задачи:
- Принятие решения, какой объект создавать.
- Создание объекта, причем при создании объекта нужно "отвязаться" от конкретного типа.
Мы будем создавать специальные объекты, которые отвечают за порождение других объектов.
A* p = new B;
// заменить на
A* p = cr->create();
# 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.
Мы принимаем решения о том, какой объект создавать, во время выполнения программы (мы можем в время выполнения программы менять своё решение, во время работы может меняться поведение системы);
Один раз создав продукт, в дальнейшем мы его можем только возвращать.
# 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 предоставляет метод для регистрации creator-ов.
В одном месте принимаем решение, в другом - создаем объект. Отвязываем код от создания конкретных объектов.
На основе чего он может принять решение? Solution должен быть независим от реализации, от конкретного набора классов.
Идея: мы создаем карту продуктов, которые у нас существуют, при добавлении класса регистрируем новый продукт в этой карте, далее, используя эту карту, осуществляем выбор.
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'ы такие же