Шаблон прототипа
![]() | В этой статье есть несколько проблем. Пожалуйста, помогите улучшить его или обсудите эти проблемы на странице обсуждения . ( Узнайте, как и когда удалять эти шаблонные сообщения )
|
Шаблон прототипа — это шаблон творческого проектирования при разработке программного обеспечения . Он используется, когда типы создаваемых объектов определяются прототипным экземпляром , который клонируется для создания новых объектов. Этот шаблон используется для того, чтобы избежать подклассов создателя объекта в клиентском приложении, как это делает шаблон фабричного метода , а также для того, чтобы избежать неизбежных затрат на создание нового объекта стандартным способом (например, с использованием ключевого слова ' new '), когда он является непомерно дорогим для данного приложения.
Для реализации шаблона клиент объявляет абстрактный базовый класс , который определяет чистый виртуальный метод clone() . Любой класс, которому требуется возможность « полиморфного конструктора », наследуется от абстрактного базового класса и реализует операцию clone() .
Клиент вместо того, чтобы писать код, который вызывает оператор «новый» для жестко запрограммированного имени класса, вызывает метод clone() прототипа, вызывает фабричный метод с параметром, обозначающим конкретный желаемый производный класс , или вызывает clone() с помощью какого-либо механизма, предусмотренного другим шаблоном проектирования.
Митотическое деление клетки, в результате которого образуются две идентичные клетки, является примером прототипа, который играет активную роль в копировании самого себя и, таким образом, демонстрирует паттерн прототипа. Когда клетка делится, образуются две клетки одинакового генотипа. Другими словами, клетка клонирует себя. [1]
Обзор
[ редактировать ]Шаблон проектирования прототипа — один из 23 шаблонов проектирования «Банды четырех» , которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторно использовать. [2] : 117
Шаблон проектирования прототипа решает такие проблемы, как: [3]
- Как можно создавать объекты, чтобы во время выполнения можно было указать, какие объекты создавать?
- Как можно создать экземпляры динамически загружаемых классов?
Создание объектов непосредственно внутри класса, который требует (использует) эти объекты, является негибким, поскольку оно связывает класс с конкретными объектами во время компиляции и делает невозможным указать, какие объекты создавать во время выполнения.
Шаблон проектирования прототипа описывает, как решить такие проблемы:
- Определите
Prototype
объект, который возвращает свою копию. - Создавайте новые объекты, копируя
Prototype
объект.
Это позволяет настроить класс с разными Prototype
объекты, которые копируются для создания новых объектов и даже больше, Prototype
объекты можно добавлять и удалять во время выполнения.
См. также класс UML и диаграмму последовательности ниже.
Структура
[ редактировать ]Класс UML и диаграмма последовательности
[ редактировать ]
На приведенной выше UML классов диаграмме
тот Client
класс относится к Prototype
интерфейс для клонирования Product
.
Product1
класс реализует Prototype
интерфейс, создав свою копию.
Диаграмма UML последовательности показывает взаимодействие во время выполнения:
Client
вызовы объектов clone()
на prototype:Product1
объект, который создает и возвращает копию самого себя ( product:Product1
объект).
Диаграмма классов 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++ .
См. также
[ редактировать ]
Ссылки
[ редактировать ]- ^ Дуэлл, Майкл (июль 1997 г.). «Непрограммные примеры шаблонов проектирования». Журнал «Объект» . 7 (5): 54. ISSN 1055-3614 .
- ^ Jump up to: а б с д и ж г Гамма, Эрих ; Хелм, Ричард; Джонсон, Ральф ; Влиссидес, Джон (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Аддисон-Уэсли. ISBN 0-201-63361-2 .
- ^ «Шаблон проектирования прототипа — проблема, решение и применимость» . w3sDesign.com . Проверено 17 августа 2017 г.