Jump to content

Шаблон прототипа

Шаблон прототипа — это шаблон творческого проектирования при разработке программного обеспечения . Он используется, когда типы создаваемых объектов определяются прототипным экземпляром , который клонируется для создания новых объектов. Этот шаблон используется для того, чтобы избежать подклассов создателя объекта в клиентском приложении, как это делает шаблон фабричного метода , а также для того, чтобы избежать неизбежных затрат на создание нового объекта стандартным способом (например, с использованием ключевого слова ' new '), когда он является непомерно дорогим для данного приложения.

Для реализации шаблона клиент объявляет абстрактный базовый класс , который определяет чистый виртуальный метод clone() . Любой класс, которому требуется возможность « полиморфного конструктора », наследуется от абстрактного базового класса и реализует операцию clone() .

Клиент вместо того, чтобы писать код, который вызывает оператор «новый» для жестко запрограммированного имени класса, вызывает метод clone() прототипа, вызывает фабричный метод с параметром, обозначающим конкретный желаемый производный класс , или вызывает clone() с помощью какого-либо механизма, предусмотренного другим шаблоном проектирования.

Митотическое деление клетки, в результате которого образуются две идентичные клетки, является примером прототипа, который играет активную роль в копировании самого себя и, таким образом, демонстрирует паттерн прототипа. Когда клетка делится, образуются две клетки одинакового генотипа. Другими словами, клетка клонирует себя. [1]

Шаблон проектирования прототипа — один из 23 шаблонов проектирования «Банды четырех» , которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторно использовать. [2] : 117 

Шаблон проектирования прототипа решает такие проблемы, как: [3]

  • Как можно создавать объекты, чтобы во время выполнения можно было указать, какие объекты создавать?
  • Как можно создать экземпляры динамически загружаемых классов?

Создание объектов непосредственно внутри класса, который требует (использует) эти объекты, является негибким, поскольку оно связывает класс с конкретными объектами во время компиляции и делает невозможным указать, какие объекты создавать во время выполнения.

Шаблон проектирования прототипа описывает, как решить такие проблемы:

  • Определите Prototype объект, который возвращает свою копию.
  • Создавайте новые объекты, копируя Prototype объект.

Это позволяет настроить класс с разными Prototype объекты, которые копируются для создания новых объектов и даже больше, Prototype объекты можно добавлять и удалять во время выполнения.
См. также класс UML и диаграмму последовательности ниже.

Структура

[ редактировать ]

Класс UML и диаграмма последовательности

[ редактировать ]
Пример класса UML и диаграммы последовательности для шаблона проектирования прототипа.

На приведенной выше UML классов диаграмме тот Client класс относится к Prototype интерфейс для клонирования Product. Product1 класс реализует Prototype интерфейс, создав свою копию.
Диаграмма UML последовательности показывает взаимодействие во время выполнения: Client вызовы объектов clone() на prototype:Product1 объект, который создает и возвращает копию самого себя ( product:Product1 объект).

Диаграмма классов UML

[ редактировать ]
Диаграмма классов UML , описывающая шаблон проектирования прототипа

Практические правила

[ редактировать ]

Иногда творческие шаблоны пересекаются — бывают случаи, когда либо прототип, либо абстрактная фабрика уместным будет . В других случаях они дополняют друг друга: абстрактная фабрика может хранить набор прототипов, из которых можно клонировать и возвращать объекты продукта. [2] : 126  Абстрактная фабрика, сборщик и прототип могут использовать синглтон в своих реализациях. [2] : 81, 134  Абстрактные фабричные классы часто реализуются с помощью фабричных методов (создание посредством наследования ), но их можно реализовать с помощью прототипа (создание посредством делегирования ). [2] : 95 

Часто проекты начинаются с использования фабричного метода (менее сложного, более настраиваемого, подклассы размножаются) и развиваются в сторону абстрактной фабрики, прототипа или конструктора (более гибкого, более сложного) по мере того, как дизайнер обнаруживает, где требуется большая гибкость. [2] : 136 

Прототип не требует создания подклассов, но требует операции «инициализации». Фабричный метод требует создания подклассов, но не требует инициализации. [2] : 116 

Проекты, в которых интенсивно используются шаблоны композитов и декораторов, часто также могут извлечь выгоду из Prototype. [2] : 126 

Общее руководство по программированию предполагает использование clone() метод при создании дубликата объекта во время выполнения, чтобы гарантировать, что он точно отражает исходный объект. Этот процесс, известный как клонирование объекта, создает новый объект с атрибутами, идентичными клонируемому. Альтернативно, создание экземпляра класса с использованием new Ключевое слово генерирует объект со значениями атрибутов по умолчанию.

Например, в контексте разработки системы управления транзакциями по банковским счетам может возникнуть необходимость дублировать объект, содержащий информацию о счете, для проведения транзакций с сохранением исходных данных. В таких сценариях использование clone() метод предпочтительнее использования new для создания экземпляра нового объекта.

Эта реализация C++11 основана на реализации до C++98, описанной в книге.

#include <iostream>

enum Direction {North, South, East, West};

class MapSite {
public:
  virtual void enter() = 0;
  virtual MapSite* clone() const = 0;
  virtual ~MapSite() = default;
};

class Room : public MapSite {
public:
  Room() :roomNumber(0) {}
  Room(int n) :roomNumber(n) {}
  void setSide(Direction d, MapSite* ms) {
    std::cout << "Room::setSide " << d << ' ' << ms << '\n';
  }
  virtual void enter() {}
  virtual Room* clone() const { // implements an operation for cloning itself.
    return new Room(*this);
  }
  Room& operator=(const Room&) = delete;
private:
  int roomNumber;
};

class Wall : public MapSite {
public:
  Wall() {}
  virtual void enter() {}
  virtual Wall* clone() const {
    return new Wall(*this);
  }
};

class Door : public MapSite {
public:
  Door(Room* r1 = nullptr, Room* r2 = nullptr)
    :room1(r1), room2(r2) {}
  Door(const Door& other)
    :room1(other.room1), room2(other.room2) {}
  virtual void enter() {}
  virtual Door* clone() const {
    return new Door(*this);
  }
  virtual void initialize(Room* r1, Room* r2) {
    room1 = r1;
    room2 = r2;
  }
  Door& operator=(const Door&) = delete;
private:
  Room* room1;
  Room* room2;
};

class Maze {
public:
  void addRoom(Room* r) {
    std::cout << "Maze::addRoom " << r << '\n';
  }
  Room* roomNo(int) const {
    return nullptr;
  }
  virtual Maze* clone() const {
    return new Maze(*this);
  }
  virtual ~Maze() = default;
};

class MazeFactory {
public:
  MazeFactory() = default;
  virtual ~MazeFactory() = default;

  virtual Maze* makeMaze() const {
    return new Maze;
  }
  virtual Wall* makeWall() const {
    return new Wall;
  }
  virtual Room* makeRoom(int n) const {
    return new Room(n);
  }
  virtual Door* makeDoor(Room* r1, Room* r2) const {
    return new Door(r1, r2);
  }
};

class MazePrototypeFactory : public MazeFactory {
public:
  MazePrototypeFactory(Maze* m, Wall* w, Room* r, Door* d)
    :prototypeMaze(m), prototypeRoom(r),
     prototypeWall(w), prototypeDoor(d) {}
  virtual Maze* makeMaze() const {
    // creates a new object by asking a prototype to clone itself.
    return prototypeMaze->clone();
  }
  virtual Room* makeRoom(int) const {
    return prototypeRoom->clone();
  }
  virtual Wall* makeWall() const {
    return prototypeWall->clone();
  }
  virtual Door* makeDoor(Room* r1, Room* r2) const {
    Door* door = prototypeDoor->clone();
    door->initialize(r1, r2);
    return door;
  }
  MazePrototypeFactory(const MazePrototypeFactory&) = delete;
  MazePrototypeFactory& operator=(const MazePrototypeFactory&) = delete;
private:
  Maze* prototypeMaze;
  Room* prototypeRoom;
  Wall* prototypeWall;
  Door* prototypeDoor;
};

// If createMaze is parameterized by various prototypical room, door, and wall objects, which it then copies and adds to the maze, then you can change the maze's composition by replacing these prototypical objects with different ones. This is an example of the Prototype (133) pattern.

class MazeGame {
public:
  Maze* createMaze(MazePrototypeFactory& m) {
    Maze* aMaze = m.makeMaze();
    Room* r1 = m.makeRoom(1);
    Room* r2 = m.makeRoom(2);
    Door* theDoor = m.makeDoor(r1, r2);
    aMaze->addRoom(r1);
    aMaze->addRoom(r2);
    r1->setSide(North, m.makeWall());
    r1->setSide(East, theDoor);
    r1->setSide(South, m.makeWall());
    r1->setSide(West, m.makeWall());
    r2->setSide(North, m.makeWall());
    r2->setSide(East, m.makeWall());
    r2->setSide(South, m.makeWall());
    r2->setSide(West, theDoor);
    return aMaze;
  }
};

int main() {
  MazeGame game;
  MazePrototypeFactory simpleMazeFactory(new Maze, new Wall, new Room, new Door);
  game.createMaze(simpleMazeFactory);
}

Вывод программы:

Maze::addRoom 0x1160f50
Maze::addRoom 0x1160f70
Room::setSide 0 0x11613c0
Room::setSide 2 0x1160f90
Room::setSide 1 0x11613e0
Room::setSide 3 0x1161400
Room::setSide 0 0x1161420
Room::setSide 2 0x1161440
Room::setSide 1 0x1161460
Room::setSide 3 0x1160f90

пример С++

[ редактировать ]

Обсуждение шаблона проектирования, а также полный иллюстративный пример реализации с использованием проектирования полиморфных классов представлены в аннотациях C++ .

См. также

[ редактировать ]
  1. ^ Дуэлл, Майкл (июль 1997 г.). «Непрограммные примеры шаблонов проектирования». Журнал «Объект» . 7 (5): 54. ISSN   1055-3614 .
  2. ^ Jump up to: а б с д и ж г Гамма, Эрих ; Хелм, Ричард; Джонсон, Ральф ; Влиссидес, Джон (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Аддисон-Уэсли. ISBN  0-201-63361-2 .
  3. ^ «Шаблон проектирования прототипа — проблема, решение и применимость» . w3sDesign.com . Проверено 17 августа 2017 г.
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: cd9498f6e1935c900c4c65cee8d0a599__1718307300
URL1:https://arc.ask3.ru/arc/aa/cd/99/cd9498f6e1935c900c4c65cee8d0a599.html
Заголовок, (Title) документа по адресу, URL1:
Prototype pattern - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)