Паттерн Прототип на C++
Прототип – это порождающий паттерн, который позволяет копировать объекты любой сложности без привязки к их конкретным классам.
Все классы–прототипы имеют общий интерфейс. Поэтому вы можете копировать объекты, не обращая внимания на их конкретные типы и всегда быть уверены, что получите точную копию. Клонирование совершается самим объектом-прототипом, что позволяет ему скопировать значения всех полей, даже приватных.
Особенности паттерна на C++
Сложность:
Популярность:
Признаки применения паттерна: Прототип легко определяется в коде по наличию методов clone
, copy
и прочих.
Концептуальный пример
Этот пример показывает структуру паттерна Прототип, а именно – из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
main.cc: Пример структуры паттерна
using std::string;
// Паттерн Прототип
//
// Назначение: Позволяет копировать объекты, не вдаваясь в подробности их
// реализации.
enum Type {
PROTOTYPE_1 = 0,
PROTOTYPE_2
};
/**
* Пример класса, имеющего возможность клонирования. Мы посмотрим как происходит
* клонирование значений полей разных типов.
*/
class Prototype {
protected:
string prototype_name_;
float prototype_field_;
public:
Prototype() {}
Prototype(string prototype_name)
: prototype_name_(prototype_name) {
}
virtual ~Prototype() {}
virtual Prototype *Clone() const = 0;
virtual void Method(float prototype_field) {
this->prototype_field_ = prototype_field;
std::cout << "Call Method from " << prototype_name_ << " with field : " << prototype_field << std::endl;
}
};
/**
* ConcretePrototype1 является подклассом Prototype и реализует метод Clone.
* В этом примере все элементы данных класса прототипа находятся в стеке.
* Если у вас среди членов есть указатели, например: String* name_ ,
* вам нужно будет реализовать копирующий конструктор, чтобы убедиться,
* что из метода клонирования у вас выполняется глубокое копирование.
*/
class ConcretePrototype1 : public Prototype {
private:
float concrete_prototype_field1_;
public:
ConcretePrototype1(string prototype_name, float concrete_prototype_field)
: Prototype(prototype_name), concrete_prototype_field1_(concrete_prototype_field) {
}
/**
* Обратите внимание, что метод Clone возвращает указатель на новую
* реплику ConcretePrototype1. Поэтому клиент (который вызывает метод
* клонирования) несет ответственность за освобождение этой памяти.
* Если у вас есть знания об умных указателях, вы можете предпочесть
* использовать здесь unique_ptr.
*/
Prototype *Clone() const override {
return new ConcretePrototype1(*this);
}
};
class ConcretePrototype2 : public Prototype {
private:
float concrete_prototype_field2_;
public:
ConcretePrototype2(string prototype_name, float concrete_prototype_field)
: Prototype(prototype_name), concrete_prototype_field2_(concrete_prototype_field) {
}
Prototype *Clone() const override {
return new ConcretePrototype2(*this);
}
};
/**
* В PrototypeFactory у вас есть два конкретных прототипа, по одному для каждого
* конкретного класса прототипа, поэтому каждый раз, когда вы хотите создать
* экземпляр, вы можете использовать существующий экземпляр и клонировать его.
*/
class PrototypeFactory {
private:
std::unordered_map<Type, Prototype *, std::hash<int>> prototypes_;
public:
PrototypeFactory() {
prototypes_[Type::PROTOTYPE_1] = new ConcretePrototype1("PROTOTYPE_1 ", 50.f);
prototypes_[Type::PROTOTYPE_2] = new ConcretePrototype2("PROTOTYPE_2 ", 60.f);
}
/**
* Будьте осторожны с освобождением всей выделенной памяти.
* Опять же, если у вас есть знания об умных указателях,
* здесь лучше использовать их.
*/
~PrototypeFactory() {
delete prototypes_[Type::PROTOTYPE_1];
delete prototypes_[Type::PROTOTYPE_2];
}
/**
* Обратите внимание, что вам просто нужно указать тип прототипа,
* который вы хотите, и метод создаст объект этого типа.
*/
Prototype *CreatePrototype(Type type) {
return prototypes_[type]->Clone();
}
};
void Client(PrototypeFactory &prototype_factory) {
std::cout << "Let's create a Prototype 1\n";
Prototype *prototype = prototype_factory.CreatePrototype(Type::PROTOTYPE_1);
prototype->Method(90);
delete prototype;
std::cout << "\n";
std::cout << "Let's create a Prototype 2 \n";
prototype = prototype_factory.CreatePrototype(Type::PROTOTYPE_2);
prototype->Method(10);
delete prototype;
}
int main() {
PrototypeFactory *prototype_factory = new PrototypeFactory();
Client(*prototype_factory);
delete prototype_factory;
return 0;
}
Output.txt: Результат выполнения
Let's create a Prototype 1
Call Method from PROTOTYPE_1 with field : 90
Let's create a Prototype 2
Call Method from PROTOTYPE_2 with field : 10