|
4. Прототип — Prototype
ADO в Delphi AJAX Android C++ CakePHP CMS COM CSS Delphi Flash Flex HTML Internet Java JavaScript MySQL PHP RIA SCORM Silverlight SQL UML XML Бази даних Веб-розробка Генетичні алгоритми ГІС Гітара Дизайн Економіка Інтелектуальні СДН Колір Масаж Математика Медицина Музика Нечітка логіка ООП Патерни Подання знань Розкрутка сайту, SEO САПР Сесії в PHP Системне програмування Системний аналіз Тестологія Тестування ПЗ Фреймворки Штучний інтелект
|
4. Прототип — Prototype
Чи ви коли небуть процювали із Outlook- календарем або ж з якимось іншим, що дозволяє копіювати календарні зустрічі з одного дня на інший? Для прикладу, уявімо собі, що ваш друг запланував невеличку пивну вечірку на п’ятницю, 22 жовтня, також він виділив час для вечірки із 7-мої вечора до 3-тьої ночі, поставив високий приорітет, а ще він зазначив, що вечірка в п’ятницю має бути всім до душі. Авжеж, це ж останній робочий день. Оскільки ви були запрошені, вечірка пройшла надзвичайно добре. Під кінець вечірки ваш друг вирішив вислати таке ж запрошення на наступну п’ятницю, але оскільки він уже добряче випив, для нього заповнити календарну форму видалося занадто важко. Яку можливість можна добавити в клалендар, щоби вона була використана вашим другом? Швидше за все «copy-paste» функціональність. Прототип дозволяє нам створювати копії об'єктів, що уже визначені на стадії дизайну (наприклад, список можливих типів зустрічей) або ж визначаються під час виконання програми («п’ятнична вечірка»), таким чином відпадає необхідність заповняти всі елементи об'єкту від «А до Я». Вже створені або визначені екземпляри об'єкту називаються прототипічними екземплярами (prototypical instances). Ми можемо використовувати цей дизайн патерн для копіювання екземплярів об’єктів в час виконання програми, що дозволяє нам уникати великої кількості похідних класів. Для прикладу, замість того, щоб мати такі класи як «п’ятиповерхова будівля із трикімнатними квартирами», «дев’ятиповерхова будівля із 2-3-4-кімнатними квартирами» і «дванадцятиповерхова будівля із 1-2-3-кімнатними квартирами», ми можемо мати просто 3 екземпляри класу ApartmentBlock, ініціалізовані вже з потрібними властивостями, а потім ми просто копіюємо один із екземплярів, коли нашій будівельній компанії потрібно збудувати якийсь конкретний будинок десь в місті. Іншими словами, ми можемо обійтися без написання подібного: «new ApBlockWith9FloorsAnd234Flats()» або «new ApartmentBlock(){Floors = 9; FlatsDic = {2,3,4}}».
Все, чого нам буде достатньо, це _9FloorsApBlock.Clone(). Звичайно, ми можемо поєднювати цей патерн із Фабричним Методом, таким чином ми будемо мати метод схожий на GetMe9FloorsAppBlock(), який всередині буде викликати копіювання прототипічного екземпляру. Уривок коду 4.1. Метод Сlone()
class CalendarPrototype
{
public virtual CalendarPrototype Clone()
{
var copyOfPrototype = (CalendarPrototype)this.MemberwiseClone();
return copyOfPrototype;
}
}
Конкретним прототипом є подія в календарі, яка виглядає приблизно так: Уривок коду 4.2. Подія в календарі
class CalendarEvent : CalendarPrototype
{
public Attendee[] Attendees { get; set; }
public Priority Priority { get; set; }
public DateTime StartDateAndTime { get; set; }
// Зауважимо, що метод Clone не перевантажений (покищо)
}
Клієнтський код (client code) виконується тоді, коли ваш друг відкриває календар і правою кнопкою мишки копіює подію, а потім вставляє в інше місце, таким чином автоматично помінявши дату та час початку події. Уривок коду 4.3. Клієнтський код, що використовує патерн
public class PrototypeDemo
{
public static CalendarEvent GetExistingEvent()
{
var beerParty = new CalendarEvent();
var friends = new Attendee[1];
var andriy = new Attendee { FirstName = "Andriy", LastName = "Buday" };
friends[0] = andriy;
beerParty.Attendees = friends;
beerParty.StartDateAndTime = new DateTime(2010, 7, 23, 19, 0, 0);
beerParty.Priority = Priority.High();
return beerParty;
}
public static void Run()
{
var beerParty = GetExistingEvent();
var nextFridayEvent = (CalendarEvent)beerParty.Clone();
nextFridayEvent.StartDateAndTime = new DateTime(2010, 7, 30, 19, 0, 0);
// Про цей код побалакаємо трішки нижче
nextFridayEvent.Attendees[0].EmailAddress = "andriybuday@liamg.com";
nextFridayEvent.Priority.SetPriorityValue(0);
if (beerParty.Attendees != nextFridayEvent.Attendees)
{
Console.WriteLine("GOOD: Each event has own list of attendees.");
}
if (beerParty.Attendees[0].EmailAddress ==
nextFridayEvent.Attendees[0].EmailAddress)
{
// В цьому випадку добре мати поверхневу копію кожного із учасників,
// таким чином моя адреса, ім'я і персональні дані залишаються тими ж
Console.WriteLine(
"GOOD: Update to my e-mail address will be reflected in all events.");
}
if (beerParty.Priority.IsHigh() != nextFridayEvent.Priority.IsHigh())
{
Console.WriteLine(
"GOOD: Each event should have own priority object, fully-copied.");
}
}
}
Як бачимо, мій друг зробив копію існуючої події і за допомогою чудо-функціоналу drag-and-drop змінив дату події. Оскільки я сидів із нашим другом, то помітив, що я можу змінити свою адресу через цю подію, тому я її поміняв на нову, оскільки вона у мене змінилася. Так як багато кому було погано після вечірки, то ми вирішили, що наступного разу не будемо брати аж так багато пива, але оскільки пива не буде дуже багато, то й пріорітет не може бути високим для наступної зустрічі: Уривок коду 4.4. Зміна приорітету зустрічі
nextFridayEvent.Priority.SetPriorityValue(0);
На перший погляд виглядає, що ми отримали те, що хотіли — копію існуючої події із запрошеними, приорітетом та іншими властивостями. Але коли я відкриваю стару попередню подію, то виявляється, що приорітет став нейтральним, замість того, щоб бути високим. Як так? Причина у тому, що, оскільки, ми ще не перевизначали метод Clone, для нашого прототипу було виконане поверхневе копіювання (MemberwiseClone) зазначене у базовому класі. Поверхневе копіювання (Shallow copy) копіює тільки прямі поля класу, таким чином залишає ті ж посилання, якщо поле було reference-типу, а якщо поле було value-типу, то буде нова копія. Глибоке копіювання (Deep copy) копіює ціле дерево об'єктів, таким чином, об'єкти отримають різні фізичні адреси у купі (heap). Корекція методу Clone у відповідності до наших потребДля нашого прототипу ми можемо реалізувати Clone() так, як нам заманеться, тому я можу реалізувати частково глибоке копіювання. Навіщо це потрібно? Я б не хотів, щоб моя адреса або інші персональні дані були різними для різних подій, але в той час я хочу бути певним, що коли я зробив копію події і змінив приорітет, то він буде поміняний тільки для цієї події. В дизайн патерні Прототип ми реалізуємо метод Clone. Інколи нам може знадобитися повна глибока копія, яку ми можемо досягнути шляхом ручного копіювання, що може бути складним, за допомогою рефлекшину, що може бути повільним, або за допомогою сериалізації та десериалізації в новий екземпляр об’єкту, що також може бути досить дорогим. Але часто вам може знадобитися часково повне копіювання, як у нашому прикладі. Ось чому багато мов програмування додали інтерфейс ICloneable, що має бути реалізований вами самостійно. Найбільш підходяща реалізація для нашого прикладу є якраз частково повною. Ось як перевантаження клонування може виглядати: Уривок коду 4.5. Підходящий метод Clone
public override CalendarPrototype Clone()
{
var copy = (CalendarEvent)base.Clone();
// Це дозволить нам мати інший список із посиланнями на тих же відвідувачів
var copiedAttendees = (Attendee[])Attendees.Clone();
copy.Attendees = copiedAttendees;
// Також скопіюємо приорітет
copy.Priority = (Priority)Priority.Clone();
// День і час не варто копіювати – їх заповнять
// Повертаємо копію події
return copy;
}
На консоль вивелися усі три речення із словом «GOOD» — підтвердження, що все зроблено згідно плану. Оскільки я також маю той же приклад, написаний на Java, і в дебаг режимі IDEA відображає змінні із однозначними ідентифікаторами посилань (числа після «собачок»), то я ще добавлю скріншот нижче (див. Малюнок 1). Зеленим виділено ті самі елементи із тими ж посиланнями, що і в попередній зустрічі. Червоним підкреслено, що списки людей насправді різні, і на нову подію можуть бути запрошені і інші бажаючі. Також червоним показано спеціально скопійований приорітет. Дати і події, звичайно, різні.
![]() Малюнок 1. Зображення об’єктів beerParty та nextFridayEvent в відлагоджуючому режимі IDEA На цій сторінці є ще трохи місця і для UML-діаграми нашого прикладу.
UML-діаграма 4. Прототип
По матеріалам книги Андрія Будая "Дизайн патерни – просто, як двері". Матеріал розміщується за домовленістю з автором.
Зверніть увагу на додаткові посиланняЯкщо вас цікавить...Головний розділСторінки, близькі за змістомзагрузка...
|
Сторінки, близькі за змістом ![]() Шаблони проектування програмного забезпечення (англ. software design patterns) — ефективні способи вирішення задач проектування програмного забезпечення. Шаблон не є закінченим зразком, який можна безпосередньо транслювати в програмний код. Об'єктно-орієнтований шаблон найчастіше є зразком вирішення проблеми і відображає відношення між класами та об'єктами, без вказівки на те, як буде зрештою реалізоване це відношення. |
|
Copyright © 2008—2026 Портал Знань.
При використанні матеріалів посилання, для інтернет-ресурсів — гіперпосилання, на Znannya.org обов'язкове.
Зв'язок
|
НТУУ "КПІ" Інженерія програмного забезпечення КПІ Лабораторія СЕТ |
|