Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
4. Інженерія систем і програмного забезпечення.docx
Скачиваний:
165
Добавлен:
17.07.2024
Размер:
1.14 Mб
Скачать

4.5.5. Основні патерни проєктування mvc, Abstract Factory, Facade, Decorator, Flyweight, Visitor, Observer, Proxy, Strategy, Chain of Responsibility

Патерн (шаблон) проєктування — це іменований опис проблеми та її розв’язання, яке можна використовувати у розробці інших систем. Таке визначення дає у своїй книзі «Застосування UML і шаблонів проєктування» (Applying UML and Patterns) Крег Ларман — фахівець із гнучкої методології та ітеративної розробки.

Проста аналогія для пояснення: щоби пройти крізь стіну, ми ставимо двері, а якщо потрібно перетнути річку — будуємо міст, тобто для кожного завдання використовуємо відповідне розв’язання. Потрапити на інший берег річки можна і на дерев’яному плоті — це також допоможе впоратись із завданням. Але міст — професійніше, надійніше та довговічніше рішення. Саме такі використовують команди розробників успішних продуктів.

Сучасне програмування ближче до літератури, ніж до математики та інших точних наук. Код — це твір, написаний певною мовою з прийнятими в ній правилами граматики та орфографії. Його можна написати так, аби повідомлення зрозуміло широке коло читачів. А можна — заплутати не лише їх, а й самого себе.

Хоч би якою мовою не писав програміст — йому потрібні інструменти, щоби спрощувати складні конструкції. Патерни проєктування — один із таких інструментів.

Коли використовують патерни. Необов’язково використовувати патерн у кожному рядку коду — можна тільки там, де це необхідно для простоти й «читабельності».

У програмуванні є принцип: якщо дія виконується багато разів, її потрібно автоматизувати. Патерни необхідні саме в таких випадках, коли розв’язання використовується багаторазово. Якщо ж операція проста і поодинока (наприклад, потрібно протестувати, чи виконується функція), можна обмежитися перевірним брудним кодом або базовим скриптом.

SOLID: ключові принципи об’єктно-орієнтованого програмування, які допомагають оптимізувати код та створювати гнучке програмне забезпечення. SOLID — це абревіатура від п’яти основних принципів:

Single responsibility — Принцип єдиного обов’язку

Open-closed — Принцип відкритості/закритості

Liskov substitution — Принцип підстановки Барбари Лісков

Interface segregation — Принцип поділу інтерфейсу

Dependency inversion — Принцип інверсії залежностей

  • Event Sourcing: архітектурний шаблон, який дозволяє виконувати та зберігати зміни в застосунку в однаковій послідовності.

  • Алгоритми та структури даних: інструменти, що допомагають упорядковувати, зберігати та працювати з даними оптимальним способом.

  • Test Driven Development: техніка розробки програмного забезпечення, заснована на повторенні коротких циклів.

Навіщо застосовувати патерни:

1. Патерни допомагають оптимізувати та підвищити ефективність роботи програмістів. Кодити без патернів — неначе забути, що хтось уже винайшов колесо.

2. Патерни спрощують взаємодію між програмістами. Ніхто не любить незрозумілий код — його доводиться переписувати. На автора такого коду дивляться скоса, як на марнотратника ресурсів команди.

Патерни — це універсальна мова розробників. Кожен патерн передбачає знання певної схеми. Але програмістам не потрібно описувати їх — достатньо сказати «Фабрика» чи «Компоновник». Як, наприклад, ми говоримо «Я зателефоную [телефоном]» замість «Я використовую засіб зв’язку за допомогою радіохвиль, передачі даних, дискретизації, фазового кодування та шифрування».

3. Патерни допомагають співпраці програмістів з іншими членами команди — від власника до профільних фахівців. Якісно написаний код прискорює процеси, зменшує кількість помилок та виправлень. Як зводити мости (писати код), потрібно знати тільки будівельникам (програмістам). Користувачам не цікава специфіка конструкцій опор або паль — для них достатньо швидко та безпечно перейти річку.

4. Управління продуктом зазвичай передбачає його поліпшення. Це досягається в тому числі за допомогою рефакторингу — перетвореннязміни вихідного коду, щоби він став простішим і зрозумілішим, але без зміни функціональності. У рефакторингу також не обійтися без патернів.

5. Знання патернів допомагає програмістам — як новачкам, так і досвідченим — у пошуку роботи. На технічній співбесіді менеджери з наймання часто дають завдання написати код — і звертають увагу на те, наскільки ефективно програміст його виконує. У тому числі — чи застосовує патерни там, де це є доцільним.

Шаблони проєктування програмного забезпечення (англ. software design patterns) — ефектні способи вирішення задач проєктування програмного забезпечення. Шаблон не є закінченим зразком, який можна безпосередньо транслювати в програмний код. Об'єктно-орієнтований шаблон найчастіше є зразком вирішення проблеми і відображає відношення між класами та об'єктами, без вказівки на те, як буде зрештою реалізоване це відношення.

Типи шаблонів GOF

  • Основні шаблони

  • Твірні шаблони

  • Структурні шаблони

  • Шаблони поведінки

  • Шаблони паралельних операцій

Шаблони GRASP. Також існує інша група шаблонів проєктування, що отримала назву GRASP — General Responsibility Assignment Software Patterns. Опис цих шаблонів наводить Крег Ларман у своїй книзі[1]. Шаблони GRASP формулюють найбільш базові принципи розподілу обов'язків між типами. До складу шаблонів GRASP входить 9 шаблонів:

  • Інформаційний експерт (Information Expert)

  • Творець примірників класу (Creator)

  • Низька зв'язаність (Low Coupling)

  • Високе зчеплення (High Cohesion)

  • Контролер (Controller)

  • Поліморфізм (Polymorphism)

  • Штучний (Pure Fabrication)

  • Перенаправлення (Indirection)

  • Стійкий до змін (Protected Variations)

Модель–вигляд–контролер (MVC, Модель–представлення–контролер, англ. Model-view-controller) — архітектурний шаблон, який використовується під час проєктування та розробки програмного забезпечення.

Цей шаблон передбачає поділ системи на три взаємопов'язані частини: модель даних, вигляд (інтерфейс користувача) та модуль керування. Застосовується для відокремлення даних (моделі) від інтерфейсу користувача (вигляду) так, щоб зміни інтерфейсу користувача мінімально впливали на роботу з даними, а зміни в моделі даних могли здійснюватися без змін інтерфейсу користувача.

Мета шаблону — гнучкий дизайн програмного забезпечення, який повинен полегшувати подальші зміни чи розширення програм, а також надавати можливість повторного використання окремих компонентів програми. Крім того використання цього шаблону у великих системах сприяє впорядкованості їхньої структури та робить їх більш зрозумілими шляхом зменшення складності.

Опис та функції компонентів. У рамках архітектурного шаблону модель–вигляд–контролер (MVC) програма поділяється на три окремі, але взаємопов'язані частини з розподілом функцій між компонентами. Модель (Model) відповідає за зберігання даних та їхню структуру. Вигляд (View) відповідальний за представлення цих даних користувачеві, тобто інтерфейс програми. Контролер (Controller) керує компонентами, отримує сигнали у вигляді реакції на дії користувача (зміна положення курсора миші, натискання кнопки, ввід даних в текстове поле) і передає дані у модель.

  • Модель є центральним компонентом шаблону MVC і відображає поведінку застосунку, незалежну від інтерфейсу користувача. Модель стосується прямого керування даними, логікою та правилами застосунку.

    • Активна модель - вигляд відстежує зміни в моделі та реагує на них.

    • Пасивна модель - вигляд оновлюється через контролер.

  • Вигляд може являти собою будь-яке представлення інформації, одержуване на виході, наприклад графік чи діаграму. Одночасно можуть співіснувати кілька виглядів (представлень) однієї й тієї ж інформації, наприклад гістограма для керівництва компанії й таблиці для бухгалтерії.

  • Контролер одержує вхідні дані й перетворює їх на команди для моделі чи вигляду.

Модель інкапсулює ядро даних і основний функціонал їхньої обробки та не залежить від процесу вводу чи виводу даних.

Вигляд може мати декілька взаємопов'язаних областей, наприклад різні таблиці та поля форм, в яких відображаються дані.

У функції контролера входить відстеження визначених подій, що виникають в результаті дій користувача. Контролер дозволяє структурувати код шляхом групування пов'язаних дій в окремий клас. Наприклад у типовому MVC-проєкті може бути користувацький контролер, що містить групу методів, пов'язаних з управлінням обліковим записом користувача, таких як реєстрація, авторизація, редагування профілю та зміна пароля.

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

Концепція MVC вперше застосувалася при проєктуванні мови програмування Smalltalk як модель для інтерфейсу користувача. Також в область застосування концепції входить реалізація каркаса документ-вид (Document-View) в рамках бібліотеки MFC[3] для мови Visual C++[4]. У сучасних технологіях концепція MVC представлена схемою JSP Model 1/2 для динамічної обробки Web-змісту на основі Java Server Pages (JSP).

У випадку застосування такої моделі при написанні програмного забезпечення для мікроконтролерів, розподіл задач виглядатиме наступним чином:

  1. Контролер відповідає за отримання та опрацювання сигналів від вхідних інтерфейсів. Для прикладу, коли, необхідно розрізнити довге та коротке натиснення кнопки, контролер здійснює цю задачу, будь-якими доступними в мікроконтролері засобами та передає закодований сигнал в компоненту Модель. Для даного прикладу Контролер може передати один з трьох кодових значень: BUTTON_IDLE, BUTTON_SHORT_CLICK, BUTTON_LONG_CLICK.

  2. Компонента Модель у випадку мікроконтролерів по суті реалізує поведінку пристрою в залежності від поточного стану та інформації отриманої з компоненти Контролер.

  3. Компонента Вигляд у випадку мікроконтролерів це типово є пристрій візуалізації інформації чи певний виконавчий пристрій. Прикладами може бути рідкокристалічний дисплей чи кроковий двигун. Для прикладу, якщо мікроконтролер здійснює керування кроковим двигуном, то компонента вигляд повинна отримувати команду з параметрами DIRECTION та STEPS_COUNT. Для прикладу перший параметр задаватиме напрямок обертання осі двигуна, а другий — кількість кроків. Детальна генерація сигналів, яка потрібна для керування двигуном захована всередині компоненти.

Абстрактна фабрика (англ. Abstract Factory) — шаблон проєктування, що забезпечує інкапсуляцію окремих фабрик під єдиною схемою, упускаючи їхню деталізацію. Належить до класу твірних шаблонів.

В типових випадках застосування, клієнтський код створює конкретну реалізацію абстрактної фабрики, а потім використовує загальний універсальний інтерфейс фабрики, для створення екземплярів об'єктів, які є частиною схеми. Клієнтський код не знає (або не бере до уваги), які саме конкретно об'єкти він отримує від цих фабрик, оскільки він використовує універсальний інтерфейс для їхнього створення. Шаблон розмежовує деталі реалізації множини об'єктів від їхнього загального використання в коді, оскільки створення об'єкта здійснюється за допомогою методів, що забезпечуються інтерфейсом фабрики.

Призначення. Подає інтерфейс для утворення родин взаємозв'язаних або взаємозалежних об'єктів, не специфікуючи їхніх конкретних класів.

Застосування. Слід використовувати шаблон Абстрактна фабрика коли:

  • система не повинна залежати від того, як утворюються, компонуються та представляються вхідні до неї об'єкти;

  • вхідні до родини взаємозв'язані об'єкти повинні використовуватися разом і необхідно забезпечити виконання цього обмеження;

  • система повинна конфігуруватися однією з родин складаючих її об'єктів;

  • треба подати бібліотеку об'єктів, розкриваючи тільки їхні інтерфейси, але не реалізацію.

Структура:

  • AbstractFactory — абстрактна фабрика:

    • оголошує інтерфейс для операцій, що створюють абстрактні об'єкти-продукти;

  • ConcreteFactory — конкретна фабрика:

    • реалізує операції, що створюють конкретні об'єкти-продукти;

  • AbstractProduct — абстрактний продукт:

    • оголошує інтерфейс для типу об'єкта-продукту;

  • ConcreteProduct — конкретний продукт:

    • визначає об'єкт-продукт, що створюється відповідною конкретною фабрикою;

    • реалізує інтерфейс AbstractProduct;

  • Client — клієнт:

    • користується виключно інтерфейсами, котрі оголошенні у класах AbstractFactory та AbstractProduct.

Переваги:

  • ізолює конкретні класи;

  • спрощує заміну сімейств продуктів;

  • гарантує сполучуваність продуктів.

Недоліки:

  • Важко добавити підтримку нового виду продуктів

Відносини:

  • Зазвичай під час виконання створюється єдиний екземпляр класу ConcreteFactory. Ця конкретна фабрика створює об'єкти продукти, що мають досить визначену реалізацію. Для створення інших видів об'єктів клієнт повинен користуватися іншою конкретною фабрикою;

  • AbstractFactory передоручає створення об'єктів продуктів своєму підкласу ConcreteFactory.

Фасад (Facade) — шаблон проєктування, призначений для об'єднання групи підсистем під один уніфікований інтерфейс, надаючи доступ до них через одну точку входу. Це дозволяє спростити роботу з підсистемами. Фасад належить до структурних шаблонів проєктування.

Класи, з яких складається шаблон можна розділити на 3 частини:

  • фасад;

  • підсистеми;

  • клієнти.

Ролі складових. Фасад:

  • Визначає певним підсистемам інтерфейс, отже знає кому адресувати запити;

  • делегує запити клієнтів потрібним об'єктам підсистеми;

  • створює нові методи, котрі об'єднують виклики об'єктів системи і\або додають свою логіку;

  • приховує підсистеми;

  • зменшує кількість параметрів методів, шляхом попередньої підстановки визначених значень.

Підсистема:

  • реалізує функціонал, закритий та не видимий для зовнішніх компонентів

  • виконує роботу, запитану клієнтом через фасад.

  • не зберігає посилання на фасад — це означає що одна підсистема може мати довільну кількість фасадів.

Клієнт:

  • здійснює запити фасаду;

  • не знає про існування підсистем.

Переваги:

  • Приховує реалізацію підсистеми від клієнтів, що полегшує використання підсистеми.

  • Сприяє слабкій взаємодії між підсистемою та її клієнтами. Це дозволяє змінити класи, які включають підсистему, не впливаючи на клієнтів.

  • Зменшує компіляційні залежностей у великих програмних системах.

  • Спрощує системи перенесення на інші платформи, оскільки менш імовірно, що для побудови однієї підсистеми потрібно побудувати всі інші.

Недоліки:

  • Не заважає сучасним клієнтам отримувати доступ до базових класів

  • Фасад не додає жодної функції, він просто спрощує інтерфейси.

Фасад використовується у випадках, коли потрібно:

  • спростити доступ до складної системи;

  • створити рівні доступу до системи;

  • додати стійкість до змін підсистем;

  • зменшити кількість сильних зв'язків між клієнтом та підсистемою, але залишити доступ до повної функціональності.

Зв'язок з іншими патернами. Фасад створює новий інтерфейс доступу, адаптер — використовує старий.

Декоратор (фр. décorateur) — структурний шаблон проєктування, призначений для динамічного підключення додаткових можливостей до об'єкта. Шаблон Decorator надає гнучку альтернативу методу визначення підкласів з метою розширення функціональності.

Завдання. Об'єкт, який передбачається використовувати, виконує основні функції. Проте може виникнути потреба додати до нього деяку додаткову функціональність, яка виконуватиметься до і/або після основної функціональності об'єкта.

Спосіб вирішення. Декоратор передбачає розширення функціональності об'єкта без визначення підкласів.

Учасники. Клас ConcreteComponent — клас, в який за допомогою шаблону Декоратор додається нова функціональність. В деяких випадках базова функціональність надається класами, похідними від класу ConcreteComponent. У подібних випадках клас ConcreteComponent є вже не абстрактним, а конкретним. Абстрактний клас Component визначає інтерфейс для використання всіх цих класів.

Переваги:

  • Декоратори забезпечують гнучку альтернативу підкласу для розширення функціональності

  • Декоратори дозволяють модифікувати поведінку під час виконання, а не повертатися до існуючого коду та вносити зміни

  • Декоратори - це хороше рішення для перестановки завдань, тому що ви можете загорнути компонент з будь-якою кількістю декораторів

  • Шаблон декоратора підтримує принцип, що класи повинні бути відкриті для розширення, але закриті для модифікації

Недоліки:

  • Декоратори можуть призвести до багатьох невеликих об'єктів у нашому дизайні, і надмірне використання може бути складним

  • Декоратори можуть викликати проблеми, якщо клієнт сильно залежить від компонентів конкретного типу

  • Декоратори можуть ускладнити процес аналізу компонента, оскільки вам потрібно не лише інвентувати компонент, але і обернути його кількома декораторами

  • Може бути складно, щоб декоратори відслідковували інших декораторів, тому що повертатися назад до декількох шарів ланцюга декораторів починає натискати шаблон декоратора поза його справжнім наміром

Наслідки. Функціональність, що додається, реалізується в невеликих об'єктах. Перевага полягає в можливості динамічно додавати цю функціональність до або після основної функціональності об'єкта ConcreteComponent.

Зв'язок з іншими патернами:

  • Стратегія змінює реалізацію, декоратор — доповнює

  • Ланцюжок обов’язків та Декоратор виконують операції через серію пов’язаних об’єктів. Але Ланцюжок обов’язків може виконувати довільні дії, незалежні одна від одної, а також у будь-який момент переривати виконання, а декоратори розширюють певну дію, не ламаючи інтерфейс базової операції і не перериваючи виконання інших декораторів.

Реалізація. Створюється абстрактний клас, що представляє як початковий клас, так і нові функції, що додаються в клас. У класах-декораторах нові функції викликаються в необхідній послідовності — до або після виклику подальшого об'єкта.

Зауваження і коментарі:

  • Хоча об'єкт-декоратор може додавати свою функціональність до або після функціональності основного об'єкта, ланцюжок створюваних об'єктів завжди повинен закінчуватися об'єктом класу ConcreteComponent.

  • Базові класи мови Java широко використовують шаблон Декоратор для організації обробки операцій введення-виведення.

У програмуванні, легковаговик (англ. flyweight pattern) — шаблон проєктування, за яким створюється об'єкт, що мінімізує використання пам'яті розподіляючи стільки даних скільки це можливо між іншими подібними об'єктами; це спосіб використання великої кількості об'єктів, коли просте повторювання їх представлення зайняло б неприпустиму кількість пам'яті. Часто деякі частини, що зберігають стан об'єктів можуть бути спільними, і загальною практикою є тримати їх у деякий зовнішній структурі даних і передавати їх до об'єктів тимчасово коли вони використовуються.

Класичним прикладом застосування шаблону легковаговик це структури даних для графічного представлення символів при відображенні тексту. Для цього необхідно буде мати, для кожного символу в документі, об'єкт гліфу що містить контури шрифту, метрику і інші дані про форматування, але це буде займати сотні або тисячі байт для кожного символу. Замість представлення таким чином кожного символу, там можна задати посилання на легкий, щодо використання пам'яті, об'єкт із гліфом для кожного екземпляру однакового символу в документі; і в результуючому об'єкті доведеться зберігати лише тільки позицію кожного символу (в документі і/або на сторінці). Іншим прикладом застосування цього шаблону є метод стиснення текстових рядків шляхом інтернування.

Призначення. Використовується для ефективної підтримки (в першу чергу для зменшення затрат пам'яті) великої кількості дрібних об'єктів.

Опис. Шаблон Легковаговик (Flyweight) використовує загальнодоступний легкий об'єкт (flyweight, легковаговик), який одночасно може використовуватися у великій кількості контекстів. Стан цього об'єкта поділяється на внутрішній, що містить інформацію, незалежну від контексту, і зовнішній, який залежить або змінюється разом з контекстом легковаговика. Об'єкти клієнтів відповідають за передачу зовнішнього стану легковаговика, коли йому це необхідно.

Переваги:

  • Зменшує кількість об'єктів, що підлягають обробці.

  • Зменшує вимоги до пам'яті.

Недоліки:

  • Переміщення стану поза об'єктом розбиває інкапсуляцію та може бути менш ефективним, ніж збереження власного поля.

Застосування. Шаблон Легковаговик можна використовувати коли:

  • В програмі використовується велика кількість об'єктів.

  • Затрати на збереження високі через велику кількість об'єктів.

  • Більшість станів об'єктів можна зробити зовнішніми.

  • Велика кількість груп об'єктів може бути замінена відносно малою кількістю загальнодоступних об'єктів, однократно видаливши зовнішній стан.

  • Програма не залежить від ідентичності об'єктів. Оскільки об'єкти-легковаговики можуть використовуватися колективно, то тести на ідентичність будуть повертати значення "істина" ("true") для концептуально різних об'єктів.

Відвідувач (Visitor) — шаблон проєктування, який дозволяє відділити певний алгоритм від елементів, на яких алгоритм має бути виконаний, таким чином можливо легко додати або ж змінити алгоритм без зміни елементів системи. Практичним результатом є можливість додавання нових операцій в існуючі структури об'єкта без зміни цих структур.

Відвідувач дозволяє додавати нові віртуальні функції в родинні класи без зміни самих класів, натомість, один відвідувач створює клас, який реалізує всі відповідні спеціалізації віртуальної функції. Відвідувач приймає посилання на елемент й реалізується шляхом подвійної диспетчеризації.

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

Шаблон Відвідувач використовується, коли:

  • існує багато об'єктів різних класів з різними інтерфейсами, і потрібно виконати операцію над цими об'єктами, залежно від їх типу.

  • потрібно виконувати над об'єктами багато не пов'язаних між собою операцій, але не хочеться "забруднювати" цими операціями класи об'єктів. При цьому, якщо ці об'єкти використовуються у декількох проєктах, з'являється можливість включати операції лише в ті проєкти, де вони необхідні.

  • класи, які визначають структуру даних, змінюються рідко, але є потреба часто визначати нові операції над цією структурою. Якщо ж класи, які визначають структуру даних, змінюються часто, краще визначити операції в цих класах, адже при зміні інтерфейсів у класі даних необхідно змінювати і класи, які реалізують операції над цією структурою.

  • треба визначити операцію над деякою структурою, не змінюючи клас цієї структури.

Опис шаблону. Основним призначенням шаблону Відвідувач є введення абстрактної функціональності для сукупної ієрархічної структури об'єктів «елемент», а саме, шаблон Відвідувач дає змогу, не змінюючи класи елементів, додавати в них нові операції. Для цього вся обробна функціональність переноситься з самих класів елементів в ієрархію спадкування Відвідувача. Шаблон відвідувач дає змогу легко додавати нові операції — потрібно просто додати новий похідний від відвідувача клас. Однак, шаблон Відвідувач слід використовувати тільки в тому випадку, якщо підкласи елементів сукупної ієрархічної структури залишаються стабільними (незмінними). В іншому випадку, потрібно докласти значних зусиль на оновлення всієї ієрархії.

Іноді наводяться заперечення з приводу використання шаблону Відвідувач, оскільки він розділяє дані та алгоритми, що суперечить концепції об'єктно-орієнтованого програмування. Однак успішний досвід застосування STL, де поділ даних і алгоритмів покладено в основу, доводить можливість використання Відвідувача.

Реалізація. Особливості шаблону:

  • Сукупна структура об'єктів елементу може визначатися за допомогою патерну Компонувальник (Composite).

  • Для обходу може використовуватися Ітератор (Iterator).

  • Шаблон Відвідувач демонструє класичний прийом відновлення інформації про втрачені типи, не вдаючись до понижуючого приведення типів (динамічне приведення).

  • Використовує механізм багаторазової диспетчеризації.

Частини шаблону:

  • Відвідувач (зазвичай, абстрактний клас чи інтерфейс)

    • визначає дію над кожним класом конкретних елементів. Ім'я та сигнатура операції мають визначати конкретний клас даних, елемент якого треба відвідати. Це дає можливість відвідувачу доступатися до елементів через інтерфейс конкретного класу.

  • Конкретний відвідувач (конкретний клас, що наслідує Відвідувач)

    • реалізує чи перевизначає операції, визначені в базовому класі. Містить алгоритми, які виконуватимуться над об'єктами відповідного класу. Також даний клас утримує локальний стан алгоритмів (цей стан, зазвичай, утримує проміжні результати під час обходу структури, та ін.).

  • Елемент (зазвичай, абстрактний клас чи інтерфейс)

    • визначає операцію, яка приймає об'єкт відвідувача як аргумент.

  • Конкретний елемент (конкретний клас, що наслідує Елемент)

    • визначає операцію, що приймає об'єкт відвідувача як аргумент.

  • Структура елементів (клас, що реалізує структуру елементів)

    • може перераховувати елементи, які містить.

    • надає високорівневий інтерфейс, що дозволяє відвідувачу виконувати елементи.

    • може бути Компонувальником чи колекцією (як список або черга).

Взаємодія:

  • Клієнт, що використовує Відвідувача, має створити об'єкт Конкретного відвідувача та обійти потрібні елементи, відвідуючи кожен з них цим об'єктом.

  • Коли елемент відвідується, він викликає операцію Відвідувача, що відповідає класу елементу. Елемент передає себе як аргумент в цю операцію щоб дозволити Відвідувачу доступ до його стану, якщо це необхідно.

Переваги:

  • спрощується додавання нових операцій;

  • об'єднання споріднених операції в класі Visitor;

  • клас Visitor може запам’ятовувати в собі якийсь стан під час обходу контейнера.

Недоліки:

  • важке додавання нових класів, оскільки потрібно оновлювати ієрархію відвідувачів.

Спостерігач, Observer — поведінковий шаблон проєктування. Також відомий як «підлеглі» (Dependents), «видавець-передплатник» (Publisher-Subscriber).

Призначення. Визначає залежність типу «один до багатьох» між об'єктами таким чином, що при зміні стану одного об'єкта всіх залежних від нього сповіщають про цю подію.

Переваги:

  • Він підтримує принцип вільного зв'язку між об'єктами, які взаємодіють один з одним

  • Дозволяє ефективно передавати дані іншим об'єктам, без будь-яких змін у класах Subject або Observer

  • Спостерігачі можуть бути додані / видалені в будь-який момент часу.

Недоліки:

  • Інтерфейс Observer повинен бути впроваджений ConcreteObserver, який передбачає успадкування. Композиції для композиції немає, оскільки інтерфейс Observer може бути екземплятором.

  • Якщо це неправильно реалізовано, спостерігач може додати складність і призвести до ненавмисних проблем із продуктивністю.

  • У програмному застосуванні повідомлення іноді можуть бути невибагливими і призвести до умов перегонів або непослідовності.

При реалізації шаблону «спостерігач» зазвичай використовуються такі класи:

  • Subject — інтерфейс, що визначає методи для додавання, видалення та оповіщення спостерігачів.

  • Observer — інтерфейс, за допомогою якого спостережуваний об'єкт оповіщає спостерігачів.

  • ConcreteSubject — конкретний клас, який реалізує інтерфейс Subject.

  • ConcreteObserver — конкретний клас, який реалізує інтерфейс Observer.

При зміні спостережуваного об'єкту, оповіщення спостерігачів може бути реалізоване за такими сценаріями:

  • Спостережуваний об'єкт надсилає, кожному із зареєстрованих спостерігачів, всю потенційно релевантну інформацію (примусове розповсюдження).

  • Спостережуваний об'єкт надсилає, кожному із зареєстрованих спостерігачів, лише повідомлення про те що інформація була змінена, а кожен із спостерігачів, за необхідності, самостійно здійснює запит необхідної інформації у спостережуваного об'єкта (розповсюдження за запитом).

Шаблон «спостерігач» застосовується в тих випадках, коли система володіє такими властивостями:

  • існує, як мінімум, один об'єкт, що розсилає повідомлення

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

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

Об'єкт володіє важливими даними і на нього підписані спостерігачі. Кожен спостерігач має можливість обновити ці дані а інші спостерігачі повинні отримати про це сповіщення і обновитись в слід якщо це необхідно.

Спостерігач не повинен запитувати об'єкт з певною періодичністю, він завжди знає що його дані актуальні.

Зв'язок із іншими патернами

  • Посередник створює двосторонній зв'язок, часто незмінний. Забирає залежності між компонентами системи. Компоненти стають залежними від посередника. Спостерігач створює односторонній зв'язок, який може мінятись під час виконання програми. Таким чином одні об'єкти залежать від інших.

Шаблон Спостерігач реалізований в численних бібліотеках і системах, включаючи майже всі інструментарії графічних інтерфейсів користувача.

Шаблон Proxy (Заступник) — Шаблон проєктування. Надає об'єкт, що контролює доступ, перехоплюючи всі виклики до нього.

Проблема. Необхідно керувати доступом до об'єкта так, щоб створювати громіздкі об'єкти «на вимогу».

Вирішення. Створити сурогат громіздкого об'єкта. «Заступник» зберігає посилання, яке дозволяє заступникові звернутися до реального суб'єкта (об'єкт класу «Заступник» може звертатися до об'єкта класу «Суб'єкт», якщо інтерфейси «Реального Суб'єкта» і «Суб'єкта» однакові). Оскільки інтерфейс «Реального Суб'єкта» ідентичний інтерфейсу «Суб'єкта», так, що «Заступника» можна підставити замість «Реального Суб'єкта», контролює доступ до «Реального Суб'єкта», може відповідати за створення або видалення «Реального Суб'єкта». «Суб'єкт» визначає загальний для «Реального Суб'єкта» і «Заступника» інтерфейс, так, що «Заступник» може бути використаний скрізь, де очікується «Реальний Суб'єкт».

«Заступник» може мати і інші обов'язки, а саме:

  • віддалений «Заступник» може відповідати за кодування запиту і його аргументів і відправку закодованого запиту реальному «Суб'єктові»

  • віртуальний «Заступник» може кешувати додаткову інформацію про реального «Суб'єкта», щоб відкласти його створення

  • захищаючий «Заступник» може перевіряти, чи має клієнтський об'єкт необхідні для виконання запиту права.

Типи замісника:

  • Кеш проксі. Кеш проксі покращує продуктивність реального об'єкта, коли вони виконують довгі операції, а результат цих операцій рідко змінюється. Наприклад, реальний об'єкт містить метод, що рахує прості числа. При першому виклику проксі делегує операцію реальному об'єкту і зберігає результат повернення. При всіх наступних викликах, проксі повертає кешовану інформацію.

  • Проксі захисту. Проксі захисту, додає рівень захисту над реальним об'єктом. Наприклад, реальний об'єкт може отримати доступ до бази даних і повернути конфіденційні дані користувачу. Проксі-захисник міг би додати методи або властивості, які дозволяють клієнтському об'єкту забезпечити відповідну аутентифікацію, перш ніж повернути дані. Він також може фільтрувати дані відповідно до прав автентифікованого користувача.

  • Віддалений проксі. Віддалений проксі-сервер надає локальний об'єкт, який посилається на реальний об'єкт в іншому місці, як правило, через мережеве з'єднання. Проксі виконує необхідні дії для кодування запитів на передачу мережі і приймає результати від віддаленого ресурсу, перш ніж повертати їх клієнту. Звичайний приклад віддаленого проксі-сервера - це локальний клас, створений Visual Studio для забезпечення доступу до вебслужби.

  • Розумний проксі. Розумний проксі додає додаткову функціональність реальному об'єкта. Ця функціональність часто невидима для клієнта. Наприклад, підрахунок існуючих посилань на ресурсомісткий об'єкт таким чином, що коли лічильник досягне нуля, дані об'єкта можуть бути видалені з пам'яті. Можна також використовувати розумний проксі для логування викликів користувача до реального об'єкта.

  • Віртуальний проксі. Віртуальний проксі забезпечує спрощену версію складного об'єкта. Тільки тоді, коли потрібні дані реального об'єкта, лише тоді він створюється, забезпечуючи форму лінивої ініціалізації. Наприклад, провідник Windows, може відобразити файли, який видно на екрані. При отриманні списку файлів, ім'я файлу, розмір та інша легка для отримання інформація зберігатиметься в об'єктах проксі. Тільки при команді "перегляд документа", реальний об'єкт буде створено і заповнено повним вмістом файлу, оскільки вони доступні повільніше і потребують більше пам'яті.

Переваги:

  • віддалений замісник;

  • віртуальний замісник може виконувати оптимізацію;

  • захищає замісника;

  • «Розумне» посилання (вказівник);

Недолік: різко збільшує час відгуку.

Стратегія (англ. Strategy) — шаблон проєктування, належить до класу шаблонів поведінки. Відомий ще під іншою назвою — «Policy». Його суть полягає у тому, щоб створити декілька схем поведінки для одного об'єкту та винести в окремий клас. Шаблон Стратегія (Strategy) дозволяє міняти вибраний алгоритм незалежно від об'єктів-клієнтів, які його використовують.

Завдання. За типом клієнта (або за типом оброблюваних даних) вибрати відповідний алгоритм, який слід застосувати. Якщо використовується правило, яке не піддається змінам, немає необхідності звертатися до шаблону «стратегія».

Мотиви:

  • Програма повинна забезпечувати різні варіанти алгоритму або поведінки

  • Потрібно змінювати поведінку кожного екземпляра класу

  • Необхідно змінювати поведінку об'єктів на стадії виконання

  • Введення інтерфейсу дозволяє класам-клієнтам нічого не знати про класи, що реалізують цей інтерфейс і інкапсулюють в собі конкретні алгоритми

Спосіб вирішення. Відділення процедури вибору алгоритму від його реалізації. Це дозволяє зробити вибір на підставі контексту.

Учасники:

  • Клас Strategy визначає, як будуть використовуватися різні алгоритми.

  • Конкретні класи ConcreteStrategy реалізують ці різні алгоритми.

  • Клас Context використовує конкретні класи ConcreteStrategy за допомогою посилання на конкретний тип абстрактного класу Strategy. Класи Strategy і Context взаємодіють з метою реалізації обраного алгоритму (в деяких випадках класу Strategy потрібно надсилати запити класу Context). Клас Context пересилає класу Strategy запит, що надійшов від його класу-клієнта.

Наслідки:

  • Шаблон Strategy визначає сімейство алгоритмів.

  • Це дозволяє відмовитися від використання перемикачів і / або умовних операторів.

  • Виклик всіх алгоритмів повинен здійснюватися стандартним чином (всі вони повинні мати однаковий інтерфейс).

Реалізація. Клас, який використовує алгоритм (Context), включає абстрактний клас (Strategy), що володіє абстрактним методом, визначальним спосіб виклику алгоритму. Кожен похідний клас реалізує один необхідний варіант алгоритму.

Використання. Архітектура Microsoft WDF (Windows Driver Frameworks) заснована на цьому патерні. У кожного об'єкта «драйвер» і «пристрій» є незмінна частина, вшита в систему, в якій реєструється змінна частина (стратегія), написана в конкретній реалізації. Змінна частина може бути і зовсім порожньою, що означає, що драйвер нічого не робить, але при цьому здатний брати участь у PnP і управлінні живленням.

Бібліотека ATL містить у собі набір класів threading model, які є стратегіями (різними реалізаціями Lock / Unlock, які потім використовуються основними класами системи). При цьому в цих стратегіях використовується статичний поліморфізм через параметр шаблону, а не динамічний поліморфізм через віртуальні методи.

Призначення шаблону проєктування Стратегія. Існують системи, поведінка яких визначається відповідно до певного роду алгоритмів. Всі вони подібні між собою: призначені для вирішення спільних задач, мають однаковий інтерфейс для користування, але відрізняються тільки «поведінкою», тобто реалізацією. Користувач, налаштувавши програму на потрібний алгоритм — отримує потрібний результат.

Приклад. Є програма(інтерфейс) через яку обраховується ціна на товар для покупців у яких є знижка та ціна за сезонною знижкою — обираємо необхідний алгоритм. Об'єктно-орієнтований дизайн такої програми будується на ідеї використання поліморфізму. Результатом є набір «класів-родичів» — у яких єдиний інтерфейс та різна реалізація алгоритмів.

  • Недоліками такого алгоритму є те, що реалізація жорстко прив'язана до підкласу, що ускладнює внесення змін.

  • Вирішенням даної проблеми є використання патерну Стратегія (Strategy).

Переваги:

  • Можливість позбутися умовних операторів.

  • Клієнт може вибирати найбільш влучну стратегію залежно від вимог щодо швидкодії і пам'яті.

Недоліки:

  • Збільшення кількості об'єктів.

  • Клієнт має знати особливості реалізацій стратегій для вибору найбільш вдалої.

Зв'язок з іншими патернами:

  • Стратегія змінює реалізацію, декоратор — доповнює

  • В стратегії користувач знає про класи стратегій і міняє їх самостійно, в стані різноманітні стани приховані від користувача, а за їх заміну відповідає сам клас

  • Міст — це структурний патерн. Його компоненти зазвичай встановлюються раз і не змінюються під час виконання програми. Використовують для розділення абстракції та реалізації. Стратегія — це шаблон поведінки. Використовують коли коли алгоритми можуть замінювати один одного під час виконання програми.

  • Шаблонний метод задає кроки алгоритму, які реалізовують підкласи. Стратегія задає алгоритм який можна виконати декількома способами, до того ж вибрати ці способи на етапі виконання програми.

Висновки. Останнім часом розроблено багато мов програмування, але в кожній з них для досягнення найкращого результату роботи необхідно використовувати шаблони програмування, одним з яких є Стратегія (Strategy).

Ланцюжок відповідальностей - шаблон об'єктно-орієнтованого дизайну у програмуванні.

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

У варіаціях стандартного ланцюжка відповідальностей, деякі обробники можуть бути в ролі диспетчерів, які здатні відсилати команди в різні напрямки формуючи Дерево відподальності. У деяких випадках це можна організувати рекурсивно, коли об'єкт який оброблюється викликає об'єкт вищого рівня обробки з командою що пробує вирішити меншу частину проблеми; у цьому випадку рекурсія продовжує виконуватися поки команда не виконається, або поки дерево повністю не буде оброблене. XML-інтерпретатор (проаналізований, але який ще не було поставлено на виконання) може бути хорошим прикладом.

Цей шаблон застосовує ідею слабкого зв'язку, який розглядається як програмування у найкращих практиках.

Шаблон рекомендований для використання в умовах:

  • В розроблюваної системі є група об'єктів, які можуть обробляти повідомлення певного типу;

  • Всі повідомлення повинні бути оброблені хоча б одним об'єктом системи;

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

Переваги:

  • Відокремлює відправника запиту та його одержувачів.

  • Спрощує ваш об'єкт, оскільки він не повинен знати про структуру ланцюга та зберігати прямі посилання на його членів.

  • Дозволяє динамічно додавати або видаляти відповідальність, змінюючи учасників або замовлення ланцюга.

Недолік: важко спостерігати характеристики виконання та налагодження.

Зв'язок з іншими патернами. Ланцюжок обов’язків та Декоратор виконують операції через серію пов’язаних об’єктів. Але Ланцюжок обов’язків може виконувати довільні дії, незалежні одна від одної, а також у будь-який момент переривати виконання, а декоратори розширюють певну дію, не ламаючи інтерфейс базової операції і не перериваючи виконання інших декораторів.