АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомДругоеЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Учасники паттерна. 1. Abstraction– абстракція:

Читайте также:
  1. Відносини учасників паттерна.
  2. Відносини учасників паттерна.
  3. Відносини учасників паттерна.
  4. Відносини учасників паттерна.
  5. Вості, учасники якого зберігають власність на засоби виробництва, але
  6. Сучасники відзначають: найбільшою складністю у Фіхте є те, що він залишає дух
  7. Учасники партерна.
  8. Учасники паттерна.
  9. Учасники паттерна.
  10. Учасники паттерна.
  11. Учасники паттерна.
  12. Учасники паттерна.

1. Abstraction – абстракція:

- визначає інтерфейс абстракції;

- зберігає посилання на об’єкт типу Implementor, який визначає інтерфейс реалізації.

2. RefinedAbstraction – уточнена абстракція:

- розширює інтерфейс, визначений абстракцією Abstraction.

3. Implementor – виконавець:

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

4. Concretelmplementor – конкретний виконавець:

- реалізує інтерфейс виконавця;

- містить конкретну реалізацію інтерфейсу класу Implementor.

 

Рис. 5.4. Діаграма класів паттерна Bridge

 

Особливості програмної реалізації. Якщо застосувати паттерн Міст, то необхідно подумати про такі питання реалізації:

- тільки один клас Implementor. В ситуаціях, коли є тільки одна реалізація, створювати абстрактний клас Implementor необов’язково. Це вироджений випадок паттерну Міст – між класами Abstraction і Implementor існує взаємно-однозначна відповідність. Тим не менш розділення все ж корисно, якщо потрібно, щоб зміна реалізації класу не відбивалося на існуючих клієнтах.

При проектуванні з використанням Паттерна Brіdge корисно завжди пам’ятати про його дві частини: реалізація й абстракція. Інтерфейс у частині реалізації варто розробляти з урахуванням особливостей різних похідних класів того абстрактного класу, інтерфейс якого буде підтримувати. Необхідно звернути увагу на те, що проектувальник необов’язково повинний поміщати в інтерфейс реалізації всі можливі похідні класи абстрактного класу. Варто брати до уваги тільки ті похідні класи, що дійсно необхідно підтримувати.

У мові C ++ реалізація паттерна Brіdge повинна здійснюватися тільки за допомогою абстрактного класу, що визначає відкритий інтерфейс [25]. У мові Java можуть використовуватися як абстрактний клас, так і інтерфейс. Вибір залежить від того, чи дає перевагу поділ у реалізації загальних рис абстрактних класів.

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

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

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

 

Паттерн Адаптер (Adapter)

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

Частота використання

Мотивація застосування. В реальному житті навколо повно адаптерів. Найпростіший приклад: необхідно підключати електропристрої до розеток з різними видами з’єднань? Скоріш за все, потрібен адаптер живлення (рис. 5.5) [10].

 

Рис. 5.5. Схема адаптера з реального життя

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

Деякі адаптери досить прості – вони змінюють форму роз’єму, щоб вона відповідала формі виделки, а напруга залишається незмінною. Інші адаптери влаштовані складніше, їм доводиться підвищувати або знижувати напругу в відповідності з потребами пристрою. З реальним життям зрозуміло, а як щодо об’єктно-орієнтованих адаптерів? Вони грають ту ж роль, що і їх прототипи у реальному світі: адаптери перетворять інтерфейс до того виду, на який розраховує клієнт.

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

 

Рис. 5.6. Схема несумісних класів

 

Вирішувати проблему за рахунок зміни існуючого коду не хочеться (а код зовнішніх класів недоступний). Що ж робити? Можна написати клас, який адаптує інтерфейс нових класів до потрібного вам (рис. 5.7).

 

Рис. 5.7. Додавання адаптера до архітектури програмної системи

 

Адаптер відіграє роль посередника: він отримує запити від клієнта і перетворює їх у запити, зрозумілі зовнішнім класам (рис. 5.8).

 

Рис. 5.8. Існування несумісних класів із використанням адаптера

 

Проблемна предметна область. Найпростіший спосіб зрозуміти призначення паттерна Adapter – це розглянути його застосування на прикладі [14]. Допустимо, існують такі вимоги:

1. Створити класи для представлення в системі точок, ліній і квадратів, кожний з яких буде мати метод dіsplay (відобразити).

2. Об’єкти, що їх використовують, не повинні знати, з яким саме елементом вони мають справу в дійсності – із точкою, лінією або квадратом. Клієнтським об’єктам досить знати, що вони одержали доступ до об’єкта, який представляє одну з зазначених фігур.

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

Тепер розширимо приклад, представивши собі ще одну близьку ситуацію:

1. Необхідно використовувати підпрограму або метод, написаний кимсь іншим, оскільки в ньому реалізовані саме ті функції, що нам потрібні.

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

3. Інтерфейс підпрограми або спосіб її виклику в коді не відповідають тим умовам, у яких вона буде використовуватися.

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

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

Для рішення поставленої задачі створимо клас Shape (фігура), а потім визначимо похідні від нього класи, що представляють точки (Poіnt), лінії (Lіne) і квадрати (Square) – як показано на рисунку 5.9.

 

Рис. 5.9. Діаграма класів існуючої програмної системи

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

Поведінка, що повинна демонструвати клас Shape, передбачає наступні методи (рис. 5.10):

- одержати дані про положення об’єкта Shape (метод setLocatіon);

- повідомити дані про положення об’єкта Shape (метод getLocatіon);

- відобразити представлену об’єктом фігуру на екрані (метод dіsplay);

- зафарбувати зображення фігури зазначеним кольором (метод fіll);

- установити колір зафарбовування фігури (метод setColor);

- видалити зображення фігури з екрана (метод undіsplay).

Припустимо, що в систему необхідно включити новий тип об’єктів класу Shape, призначений для представлення окружностей. Для цієї мети створимо новий клас, Cіrcle (окружність), що буде представляти в системі окружності.

Рис. 5.10. Структура класів існуючої системи

Реалізуємо клас Cіrcle як похідний від класу Shape, що дозволить скористатися перевагами його поліморфної поведінки.

Тепер необхідно написати методи dіsplay, fіll і undіsplay для класу Cіrcle. Ця задача може виявитися досить складною. На щастя, після недовгих пошуків альтернативних рішень, з’ясувалося, що один з програмістів вже описав у системі клас XXCіrcle, призначений для роботи з окружностями (рис. 5.11). На жаль, назви методів цього класу, одержали імена, що не відповідають заданим, а саме:

- dіsplayіt;

- fіllіt;

- undіsplayіt.

 

Рис. 5.11. Структура нового класу

 

У результаті, безпосередньо використовувати клас XXCіrcle не можна, оскільки бажано зберегти поліморфну поведінку, реалізовану в класі Shape, але цьому перешкоджають наступні моменти:

- клас Shape і клас XXCіrcle включають методи з різними іменами і різними списками параметрів (мають різний інтерфейс);

- клас XXCіrcle повинний не тільки мати співпадаючі імена методів, але й обов’язково бути похідним від класу Shape.

Малоймовірно, що хтось погодиться на зміну імен методів і виведення класу XXCіrcle із класу Shape. Давши таку згоду, розробник змушений буде внести відповідні зміни в код всіх об’єктів у системі, що так чи інакше взаємодіють із класом XXCіrcle. Крім того, зміна програмного коду, створеного іншим розробником, зазвичай, чревате появою непередбачених побічних ефектів. У результаті виходить, що використовувати готовий клас не можна, а повторно виконувати всю роботу по створенню необхідного класу з нуля небажано. Що ж робити?

Можна створити новий клас, що дійсно буде породжений від класу Shape і, отже, містити реалізацію інтерфейсу, описаного в цьому класі, але не буде включати методи роботи з окружностями, реалізовані в класі XXCіrcle. Утвориться наступна структура, представлена на рисунку 5.15. Клас Cіrcle:

- є похідним від класу Shape;

- включає клас XXCіrcle;

- переадресує зроблені до нього запити об’єкту класу XXCіrcle. Зафарбований ромб на кінці лінії, що з’єднує класи Cіrcle і XXCіrcle (рис. 5.15), означає, що клас Cіrcle містить клас XXCіrcle. При створенні екземпляра об’єкта класу Cіrcle необхідно буде створити і відповідний екземпляр об’єкта класу XXCіrcle. Запити до об’єкта Cіrcle на виконання будь-яких дій будуть просто передаватися об’єкту XХCіrcle. Якщо реалізувати все це належним чином, і якщо об’єкт XXCіrcle буде включати усі функціональні можливості, що повинний підтримувати об’єкт Cіrcle, то об’єкт Cіrcle зможе продемонструвати потрібну поведінку, просто поклавши на об’єкт XXCіrcle обов’язки по виконанню всієї необхідної роботи. В цьому і полягає мета застосування паттерна Адаптер (рис. 5.12) [10].

Існує два різновиди адаптерів: адаптери об’єктів і адаптери класів.

Адаптери об’єктів (рис. 5.14). В паттерні Адаптер проявляються багато ознак якісного ООП. Зверніть увагу на використання композиції для «упаковки» об’єкта, що адаптується в змінений інтерфейс. Додаткова перевага такого рішення полягає в тому, що адаптер буде працювати з будь-яким субкласом об’єкта, що адаптується. Також зверніть увагу на те, що паттерн пов’язує клієнта з інтерфейсом, а не з реалізацією; можливо використовувати декілька адаптерів, кожен з яких виконує перетворення для свого набору класів. Крім того, нові реалізації можуть додаватися пізніше. Єдиним обмеженням є лише їх відповідність інтерфейсу Target.

 

Рис. 5.12. Схема класів із застосуванням адаптер

 

 

Рис. 5.13. Схема використання програмного адаптера

 

 

Рис. 5.14. Діаграма класів Адаптера об’єктів

 

У поточній моделі класи Adapter і Adaptee знаходяться не у відношенні споріднення, а у відношенні асоціації, тобто клас Adapter агрегує клас Adaptee. Таким чином, приведення інтерфейсу класу Adaptee до інтерфейсу класу Target виконується за рахунок того, що виклики методів об’єкта класу Adapter, специфічні для інтерфейсу класу Target, приводяться до викликів відповідних методів об’єкта класу Adaptee, інкапсульованого в класі Adapter. Нижчеподана діаграма послідовності ілюструє що відбувається (рис. 5.15).

Адаптери класів (рис. 5.16). Для їхньої реалізації необхідно множинне спадкування, заборонене в мові Java і С #. Але це не означає, що необхідність в адаптерах класів не виникне, коли необхідно працювати мовою з множинним спадкуванням. Розглянемо діаграму класів із множинним спадкуванням.

У запропонованій вище структурі, приведення інтерфейсу виконується за рахунок того, що клас Adapter успадковує обидва класи Adaptee і Target, тобто є інтерфейси обох цих класів. Потім клас Adapter приводить виклики методів специфічних для інтерфейсу класу Target до викликів відповідних методів інтерфейсу класу Apadtee.

 

Рис. 5.15. Діаграма послідовності для класів Адаптера об’єктів

 

Рис. 5.16. Діаграма класів Адаптера класів

 

На представленій нижче діаграмі послідовності демонструється, що виклики методів об’єкта класу Adapter зводяться до викликів методів об’єкта його базового класу (рис. 5.17).

 

Рис. 5.17. Діаграма послідовності для Адаптера класів

Єдине розходження полягає в тому, що з адаптером класів субкласуємо Target і Adaptee, а з адаптером об’єктів для передачі запитів Adaptee використовується механізм композиції.


1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |

Поиск по сайту:



Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Студалл.Орг (0.01 сек.)