Прототип (Prototype)
Назначение
Облегчает динамическое создание путем определения классов, объекты которых могут создавать свои дубликаты.
Описание
Прототип - это объект, который может быть использован в качестве образца для создания точно такого же объекта с точно такими же значениями полей (клона). Более того: если в переменной типа Prototype хранится объект класса ConcretePrototype, то клонируется именно этот объект.
В стандартной библиотеке .NET имеется интерфейс ICloneable с единственным методом Clone(), предназначенном для этих целей. Реализация метода Clone() возлагается на класс. Однако, в классе Object есть protected-метод MemberwiseClone(), который выполняет неглубокое клонирование (клонирование подобъектов только первого уровня), что в большинстве случаев достаточно.
Использование
Паттерн Прототип используется когда:
- Инстанцируемые классы определяются во время выполнения
- Экземпляры класса могут находиться в одном из не очень большого числа состояний
Реализация
Диаграмма классов
Участники
- Prototype - прототип
Объявляет интерфейс клонирования
- ConcretePrototype - конкретный прототип
Реализует интерфейс клонирования
- Client - клиент
Создает новый объект с запросом к прототипу клонировать себя
Пример
Поскольку пример паттерна Прототип сам по себе простой. Поэтому ниже приводится пример с MazeGame, в котором абстрактная фабрика заменена на прототипную фабрику. Если абстрактная фабрика требует порождения подкласса фабрики для конструирования объектов других типов, то прототипная фабрика инициализируется объектами-прототипами. и ее подкласс создавать не нужно.
Код
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MazeCommon;
using MazeGameAbstractFactory;
using MazeGame = MazeGameAbstractFactory.MazeGame;
namespace MazegamePrototype
{
class MazePrototypeFactory: MazeFactory
{
private Room prototypeRoom;
private Wall prototypeWall;
private Door prototypeDoor;
public MazePrototypeFactory(Wall w, Room r, Door d)
{
prototypeWall = w;
prototypeRoom = r;
prototypeDoor = d;
}
public override Wall MakeWall()
{
return (Wall)prototypeWall.Clone();
}
public override Door MakeDoor(Room r1, Room r2)
{
Door d = (Door)prototypeDoor.Clone();
d.Initialize(r1,r2);
return d;
}
public override Room MakeRoom(int n)
{
Room r = (Room)prototypeRoom.Clone();
r.RoomNumber = n;
return r;
}
}
class ProgramPrototype
{
static void Main(string[] args)
{
Console.WriteLine("Prototype");
MazeGame game = new MazeGame();
MazePrototypeFactory f = new MazePrototypeFactory(new Wall(),new Room(1),new Door());
game.CreateMaze(f);
}
}
}
Достоинства и недостатки
- Сокрытие истинного типа клонируемого объекта - гибкость для клиента
- Недостаток: клонирование объекта при наличии круговых ссылок - достаточно сложная задача.
Варианты
- Если число прототипов невелико, то можно вести реестр прототипов с помощью диспетчера прототипов (паттерн Пул объектов).
- В некоторых случаях необходимо клонировать весь объект, в других, достаточно создать клон по типу объекта с полями, заполненными значениями по умолчанию.