Прототип (шаблон проєктування)Прототип (англ. Prototype) — твірний шаблон проєктування, який дозволяє створювати копії існуючих об'єктів таким чином, що програмний код не залежить від їх класів. Цей шаблон застосовують у випадку, коли тип об'єктів, що створюються заданий екземпляром прототипу, що використовується для створення нових об'єктів шляхом копіювання цього прототипу. Він використовується для:
Для реалізації цього шаблону, необхідно визначити абстрактний базовий клас (інтерфейс), який визначає чисто віртуальний метод clone(). Будь-який клас, що потребує виконання функціоналу «поліморфного конструктора» буде успадковувати цей базовий абстрактний клас, і реалізовувати операцію clone(). Клієнт, замість того, щоб писати код, який би використовував оператор «new» із жорстко вказаним іменем класу, викликає метод clone() для прототипу. Прикладом ситуації, що демонструє шаблон Прототип є мітотичний поділ живих клітин — що призводить до створення двох ідентичних клітин — тут прототип відіграє важливу роль в створенні повної копії самого себе. Коли відбувається ділення клітини, в результаті утворюються дві ідентичні клітини із ідентичним генотипом. Іншими словами, клітина клонує сама себе.[1] ОписШаблон проектування Прототип [2] є одним із двадцяти трьох добре відомих шаблонів, із книги Банди чотирьох (GoF) який описує як вирішити рекурсивні задачі проектування, для створення гнучкого і придатного для повторного використання об'єктно-орієнтованого програмного забезпечення, у такий спосіб, щоб об'єкти було легше реалізовувати, змінювати, тестувати, і повторно використовувати. ПризначенняЗадає види об'єктів, що створюються, за допомогою екземпляру-прототипу, та створює нові об'єкти шляхом копіювання цього прототипу. Шаблон проектування Прототип дозволяє вирішити такі проблеми як: [3]
Створення нових об'єктів напряму за допомогою класу, що потребує (використовує) певні об'єкти є не достатньо гнучким, оскільки змушує прив'язати клас до певних об'єктів під час компіляції, і стає неможливо визначити які об'єкти будуть створені під час виконання коду. Шаблон проектування прототип визначає спосіб, як вирішити цю задачу:
Це дозволяє створювати конфігурації класу із різними об'єктами прототипу ЗастосуванняСлід використовувати шаблон Прототип коли:
Емпіричні правилаІноді твірні шаблони збігаються один з одним — існують випадки, коли більш доречно використовувати або прототип або абстрактну фабрику. А в інших випадках вони доповнюють один одного: абстрактна фабрика може зберігати множину прототипів, з яких буде створюватися копія і повертатимуться утворені об'єкти (GoF, с. 126). Абстрактна фабрика, будівник, і прототип можуть в своїй реалізації використовувати шаблон Одинак. (GoF, сc. 81, 134). Класи абстрактних фабрик часто реалізовані за допомогою фабричних методів (створення через успадкування), але вони можуть також реалізовуватися за допомогою прототипу (створення шляхом делегування[en]). (GoF, с.95) Часто, проектування починається із використання фабричного методу (менш складно, піддається кращому налаштовуванню, поширюється на підкласи) і еволюціонує в сторону абстрактної фабрики, прототипу, або будівника (більш гнучкі і більш складні) якщо архітектор коду виявляє де потребується більша гнучкість поведінки. (GoF, с.136) Прототип не потребує створення підкласів, але потребує процедури «ініціалізації». Фабричний метод потребує створення підкласів, але не потребу ініціалізації. (GoF, с. 116) Архітектури, які широко використовують шаблони компонувальник та декоратор, також можуть отримати вигоду із використання прототипу. (GoF, с.126) Емпіричне правило може полягати в тому, що вам може знадобитися метод копіювання clone() Об'єкту коли ви хочете створити інший об'єкт під час виконання коду, який є повною копією того об'єкта, що ви копіюєте. Повна копія означає, що всі атрибути новоствореного об'єкту будуть такими ж самими, як і в того об'єкта який ви клонуєте. Якби замість цього ви створювали екземпляр даного класу використовуючи ключове слово new, ви б отримали об'єкт із атрибутами, що мають початкові не задані значення. Наприклад, якщо ви використовуєте систему, що здійснює транзакції із банківським рахунком, тобі б вам знадобилося створити копію об'єкту, що зберігає в собі інформацію про цей рахунок, здійснити над ним транзакцію, а потім замінити оригінальний об'єкт на змінений. В даному випадку ви б захотіли використати метод clone() замість new. Структура
Переваги
Недоліки
ВідносиниКлієнт звертається до прототипу, щоб той створив свого клона. РеалізаціяПриклад С++Приклад реалізації мовою С++
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Prototype
{
virtual Prototype* Clone() = 0;
};
class Item : public Prototype
{
public:
int ID;
string Name;
public:
Item(string strName) : Name(strName)
{
ID = GetNewID();
};
// конструктор копіювання
Item(Item& item) : Name(item.Name)
{
ID = GetNewID();
};
virtual ~Item() { };
virtual Prototype* Clone()
{
return new Item(*this);
};
static int GetNewID()
{
static int ID = 0;
return ++ID;
};
};
void print(Prototype* p)
{
Item* pItem = dynamic_cast <Item*> (p);
cout << "ID: " << pItem->ID << endl;
cout << "Name: " << pItem->Name << endl;
cout << endl;
}
void main()
{
vector<Prototype*> items;
Item Item("Concrete Item");
// клонуємо об'єкт
for (int i = 0; i < 10; ++i)
{
items.push_back
(
Item.Clone() // виклик методу клонування
);
}
// Друкуємо клонів
for (int i = 0; i < 10; ++i)
{
print(items[i]);
}
}
Приклад С#Приклад реалізації мовою С#
using System;
using System.IO;
using System.Drawing;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
namespace PrototypePattern
{
static class Prototype
{
public static T DeepCopy<T>(this T self)
{
var formatter = new BinaryFormatter();
using (var memoryStream = new MemoryStream())
{
formatter.Serialize(memoryStream, self);
memoryStream.Seek(0, SeekOrigin.Begin);
var clone = formatter.Deserialize(memoryStream);
return (T)clone;
}
}
}
[Serializable]
class TreePrefab
{
private int _height;
private Color _color;
public TreePrefab(int height, Color color)
{
_height = height;
_color = color;
}
public void SetColor(Color color)
{
_color = color;
}
}
class Program
{
static void Main(string[] args)
{
var treePrefab = new TreePrefab(2, Color.Green);
var forest = new List<TreePrefab>();
for (int i = 0; i < 10; i++)
{
var tree = treePrefab.DeepCopy();
tree.SetColor(i % 2 == 0 ? Color.Green : Color.YellowGreen);
forest.Add(tree);
}
}
}
}
Примітки
Джерела
ЛітератураАлан Шаллоуей, Джеймс Р. Тротт. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М. : «Вильямс», 2002. — 288 с. — ISBN 0-201-71594-5. |