Дизайн-патерни: Прототип (С++)

5 хв. читання

Існує п'ять породжуючих дизайн-патернів. Ми вже розглянули три з них:

В цій статті розглядаємо дизайн-патерн «Прототип».

Прототип (Prototype)

Призначення: створення копій об'єктів, які вже визначені наперед.

Багато хто може побачити такий підхід схожим до «Будівельника». Однак «Будівельник» передбачає будування об'єкту повністю, тобто заповнення всіх його атрибутів. Натомість «Прототип» дозволяє залишити деякі або всі атрибути без змін, просто перенісши їх на новий об'єкт, який буде в загальному такий самий, але з можливістю зміни деяких особливостей.

Назва «Прототип» говорить, що даний дизайн-патерн передбачає створення об'єкта, який буде прототипом для іншого об'єкта. Тобто, цей інший об'єкт міститиме якісь дані, що містить прототип і, можливо, свої власні особливості.

Це можна пояснити на прикладі ручки. Припустимо, ви хочете виготовити ручку. Але для того щоби зекономити час і не розробляти кожного разу новий ескіз для вашої ручки, ви можете створити її прототип. Ручка має бути певної довжини та з певним діаметром. І всі ручки, які ви робите, мають однакову довжину і діаметр, але вони можуть бути різного кольору. Для цього і створюється прототип, з якого можна створити ручку – скопіювати довжину і діаметр, а колір вже задати на власний вибір.

Іншим прикладом може слугувати проектування будівлі. Спочатку потрібно описати прототип будівлі:

class BuilderPrototype
{
public:
	BuilderPrototype();
	~BuilderPrototype();

	virtual BuilderPrototype* clone() = 0;

	string getBuilderType();
	int getFloors();
	int getFlats();

protected:
	string m_builderType;
	int m_floors, m_flats;
};

Будівля може бути різного типу, містити різну кількість поверхів та квартир. Зверніть увагу на чисту віртуальну функцію clone(). Ця функція є явною ознакою прототипу. Вона повертає копію якогось конкретного прототипу.

Наступний етап – описати конкретний прототип будівлі. Нехай їх буде два: хмарочос та п'ятиповерхівка.

Хмарочос:

#include "BuilderPrototype.h"

class Skyscaper : public BuilderPrototype
{
public:
	Skyscaper();
	Skyscaper(int floors, int flats);
	~Skyscaper();

	virtual BuilderPrototype* clone();
};

П'ятиповерхівка:

#include "BuilderPrototype.h"

class FifthFloorBuilder : public BuilderPrototype
{
public:
	FifthFloorBuilder();
	FifthFloorBuilder(int flats);
	~FifthFloorBuilder();

	virtual BuilderPrototype* clone();
};

Зверніть увагу на додаткові конструктори в даних класах, за допомогою яких можна задавати кількість поверхів та квартир. Логічно, що для п'ятиповерхівки не можна задати кількість поверхів, оскільки їх повинно бути 5 у будь-якому випадку. Тому визначення такого конструктора для п'ятиповерхівки наступне:

FifthFloorBuilder::FifthFloorBuilder(int flats)
{
	m_builderType = "FifthFloorBuilder";
	m_floors = 5;
	m_flats = flats;
}

Отже, тип будівлі та кількість поверхів незмінні, а от кількість квартир можна встановлювати окремо. Це означає, що ми маємо прототип п'ятиповерхівки – у неї є 5 поверхів, вона відповідно називається. Але коли потрібно буде реалізувати цей прототип, то необхідно уточнити інший атрибут – кількість квартир, адже у кожній п'ятиповерхівці може бути різна кількість квартир.

Те ж саме і для хмарочоса, за винятком того, що кількість поверхів теж довільна. Звісно ж, їх не може бути менше як 50, бо тоді це вже не буде хмарочос, але розглядаючи дані приклади, не будемо заглиблюватися в такі деталі.

Skyscaper::Skyscaper(int floors, int flats)
{
	m_builderType = "Skyscaper";
	m_floors = floors;
	m_flats = flats;
}

Тепер необхідно перевизначити функцію clone(), котра має повертати копію власного прототипа, тому вона матиме вигляд:

BuilderPrototype* Skyscaper::clone()
{
	return new Skyscaper(*this);
}

І для п'ятиповерхівки відповідно:

BuilderPrototype* FifthFloorBuilder::clone()
{
	return new FifthFloorBuilder(*this);
}

Залишається тільки останній етап – ініціалізувати ці прототипи. Для цього створюємо клас-ініціалізатор - PrototypeInit:

#include "FifthFloorBuilder.h"
#include "Skyscaper.h"

class PrototypeInit
{
	static BuilderPrototype* prot1;
	static BuilderPrototype* prot2;
	static BuilderPrototype* prot3;

public:
	PrototypeInit();
	~PrototypeInit();

	static void initPrototype();
	static BuilderPrototype* getProt1();
	static BuilderPrototype* getProt2();
	static BuilderPrototype* getProt3();
};

Прототипи повинні бути статичними, адже прототип чогось може бути тільки один. Також клас містить статичні методи для роботи зі статичними даними.

В першу чергу необхідно ініціалізувати статичні дані. Слід зробити це у виконуваному файлі:

BuilderPrototype* PrototypeInit::prot1 = 0;
BuilderPrototype* PrototypeInit::prot2 = 0;
BuilderPrototype* PrototypeInit::prot3 = 0;

У функцій initPrototype створюються екземпляри прототипів:

void PrototypeInit::initPrototype()
{
	prot1 = new Skyscaper(84, 1034);
	prot2 = new FifthFloorBuilder(34);
	prot3 = new Skyscaper(103, 1566);
}

Тобто, реалізується якийсь прототип Skyscaper(84, 1034). Тип будівлі вже визначений як Skyscaper, а кількість поверхів і квартир задаються відповідно до потреби. Так само й у випадку з п'ятиповерхівкою - FifthFloorBuilder(34). Тип будівлі (FifthFloorBuilder) та кількість поверхів (5) вже визначені, задається тільки кількість потрібних квартир.

Тепер необхідно тільки повернути ці реалізовані прототипи. Це робиться за допомогою функції clone():

BuilderPrototype* PrototypeInit::getProt1()
{
	return prot1->clone();
}

Функція getProt1() викликає метод clone() певного об'єкта (в даному випадку об'єкта типу Skyscaper, оскільки prot1 = new Skyscaper(84, 1034);), який у свою чергу повертає копію об'єкта, що його викликав.

Використання дизайн-патерну наступне:

#include "PrototypeInit.h"

int main()
{
	PrototypeInit::initPrototype();			// ініціалізувати прототипи
	BuilderPrototype* builder;				// створити загальний прототип будинку
	builder = PrototypeInit::getProt1();	// отримати копію реалізованого прототипу хмарочоса
	cout << builder->getBuilderType() << " " << builder->getFloors() << " " << builder->getFlats() << endl;
	builder = PrototypeInit::getProt2();	// отримати копію реалізованого прототипу п'ятиповерхівки
	cout << builder->getBuilderType() << " " << builder->getFloors() << " " << builder->getFlats() << endl;
	builder = PrototypeInit::getProt3();
	cout << builder->getBuilderType() << " " << builder->getFloors() << " " << builder->getFlats() << endl;

    return 0;
}

Результат виконання:

Protoype execution

Підсумки

Дизайн-патерн «Прототип» дозволяє створювати копії об'єктів, які визначені заздалегідь.

Алгоритм застосування:

  1. Створити загальний абстрактний клас-прототип з функцією, що забезпечує копіювання об'єктів (BuilderPrototyНеpe);
  2. Створити похідні класи, які будуть конкретними прототипами, що визначають функцію копіювання та задають атрибути за допомогою конструктора (FifthFloorBuilder та Skyscaper);
  3. Реалізувати клас-ініціалізатор, який ініціалізує прототипи, використовуючи статичні дані та методи, а також повертає реалізований прототип, викликаючи функції копіювання з похідних класів в залежності від типу об'єкта (PrototypeInit);

Вихідний код до статті можна завантажити тут.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.8K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація