Паттерн Прототип на C++

Добавлено 18 апреля 2022 в 23:07

Прототип – это порождающий паттерн, который позволяет копировать объекты любой сложности без привязки к их конкретным классам.

Все классы–прототипы имеют общий интерфейс. Поэтому вы можете копировать объекты, не обращая внимания на их конкретные типы и всегда быть уверены, что получите точную копию. Клонирование совершается самим объектом-прототипом, что позволяет ему скопировать значения всех полей, даже приватных.

Особенности паттерна на 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

Теги

C++ / CppБанда четырех / Gang of Four / GoFОбъектно-ориентированное программирование (ООП)Паттерны проектирования / Design PatternsПорождающие паттерныПрограммированиеПрототип / Prototype / Клон

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.