|
|||||||
АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомДругоеЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция |
Важливі принципи ООПВажливі принципи ООП можна сформулювати наступним чином [8]: 1. Видалення аспектів додатка, які можуть змінюватися, і відокремлення їх від тих, які завжди залишаються постійними. 2. Програмування на рівні інтерфейсу, а не на рівні реалізації. 3. Віддавання переваги композиції перед спадкуванням. Розуміння спадкування як методу концептуалізації варіацій, а не механізму визначення особливих версій вже існуючих об’єктів. 4. Прагнення до підтримки низької зв’язаності в системі. 5. Повсюдне дотримання правила «один раз і тільки один раз» у відношенні реалізації зобов’язань. При проектуванні реалізації дуже важливо дотримуватися стратегії, відповідно до якої кожне правило реалізується тільки в одному місці. Іншими словами, якщо мається деяке правило, що визначає спосіб виконання будь-яких дій, воно повинно бути реалізовано тільки в одному місці. Такий підхід зазвичай приводить до одержання програмного коду з великою кількістю коротких методів. При цьому за рахунок мінімальних додаткових зусиль усувається дублювання і вдається уникнути безлічі потенційних проблем. Дублювання шкідливе не тільки через виконання додаткової роботи з багаторазового введення однакових фрагментів коду, але й у більшому ступені через імовірності того, що при деяких змінах у майбутньому модифікація буде зроблена не скрізь. Розглянемо на прикладі способи застосування описаних принципів розробки ПС. Нехай мається програмний додаток – гра, яка імітує життя качок. Проектувальники системи скористалися стандартним підходом ООП і визначили суперклас Duck, на основі якого оголошуються типи конкретних качок (рис. 2.3) [10].
Рис. 2.3. Використання принципів ООП на прикладі
Однак, для підтримки належного рівня популярності гри, було прийнято рішення внести програмні зміни до неї. Тепер необхідно було реалізувати можливість польоту качок. Можна було б додати метод fly() до класу Duck, і він буде успадкований всіма похідними класами (рис. 2.4).
Рис. 2.4. Створення ієрархії класів
Здавалося що спадкування ідеально підходить для повторного використання коду, але із супроводом виникли проблеми. Нова поведінка не притаманна всім підкласам суперкласу Duck, і тепер в грі здатність польоту набували навіть іграшкові качки. Локальна зміна коду призвела до нелокального ефекту. Можливо було перевизначити батьківський метод fly() в класах качок, які не мають здатності літати, але що робити, якщо в гру стане необхідно додати дерев’яних качок, які не літають і не крякають (рис. 2.5)?
Рис. 2.5. Додавання нових аспектів до створеної архітектури
Тобто спадкування не є оптимальним механізмом, тому що поведінка об’єктів змінюється в субкласах, а деякі аспекти поведінки містяться не у всіх субкласах. Для подібних ситуацій корисно використати принцип проектування, який рекомендує виділити аспекти додатка, які можуть змінюватися, і відокремити їх від тих, які завжди залишаються постійними. Інше формулювання того ж принципу: знайдіть змінні складові інкапсулюйте їх, щоб пізніше їх можна було змінювати або розширювати без впливу на сталі складові. Подібне ствердження здається дивним, якщо розуміти інкапсуляцію тільки як приховання даних. Але воно виглядає досить розумним, якщо під інкапсуляцією розуміти приховання конкретних класів за допомогою абстрактних. Розташування в тексті посилань на абстрактний клас дозволяє сховати його можливі варіації. У дійсності, багато паттернів проектування використовують інкапсуляцію для розподілу об’єктів по різних рівнях. Це дозволяє розробнику вносити зміни на одному рівні, не роблячи впливу на іншому. Цим досягається слабка зв’язаність між об’єктами різних рівнів. При всій своїй простоті ця концепція лежить в основі майже всіх паттернів проектування. Всі паттерни забезпечують можливість зміни деякої частини системи незалежно від інших частин. Як же спроектувати набір класів, що реалізують змінні аспекти поведінки? Щоб відокремити «змінне» від «сталого» необхідно створити два набору класів (що не залежать від Duck): один для польоту, а інших для крякання. Кожній набір класів містить реалізацію відповідної поведінки (рис. 2.6).
Рис. 2.6. Схема інкапсуляції змінних аспектів системи
Виникає питання яким чином спроектувати набір класів, які реалізують змінні аспекти поведінки. Нам необхідно отримати максимальну гнучкість додатку. Наприклад, бажано мати можливість створювати новий екземпляр класу MallardDuck і ініціалізувати його з конкретною поведінкою fly(). І було б добре передбачити можливість динамічної зміни поведінки. Тобто до класів Duck, необхідно внести методи вибору поведінки, щоб спосіб польоту MallardDuck можна були змінити в ході виконання додатку. Для цього необхідно застосувати другий принцип проектування, якій передбачає програмування на рівні інтерфейсу, а не на рівні реалізації. Для представлення кожного аспекту поведінка буде використовувати інтерфейс, а кожна реалізація аспекту поведінки буде представлена реалізацією цього інтерфейсу. На цей раз інтерфейси реалізуються не класами Duck, замість цього створюється набір класів, єдиним змістом який є надання деякої поведінки. І тепер інтерфейс поведінки реалізується класом поведінки, а не класом Duck. Такий підхід відрізняється від того, що робилося раніше, коли поведінка надавалася або конкретній реалізації в суперкласі Duck, або спеціалізованій реалізації в самому субкласі. В обох випадках виникала залежність від реалізації. В новій версії архітектури субкласи Duck використовують поведінку, надану інтерфейсом FlyBehavior або QuackBehavior (рис. 2.7).
Рис. 2.7. Схема проектування від інтерфейсу
Реалізацію від інтерфейсу можна ще раз представити наступним чином (рис. 2.8).
Рис. 2.8. Приклад реалізації від інтерфейсу Програмування на рівні реалізації виглядає так:
Dog d= new Dog(); d.bark(); Оголошення d з типом Dog потребує програмування на рівні конкретної реалізації Animal. Програмування на рівні інтерфейсу/супертипу:
Animal animal=new Dog(); animal.makeSound(); //поліморфне використання посилання Або ще краще замість жорсткої фіксації підтипу в коді new Dog(), об’єкт конкретної реалізації привласнювати під час виконання:
а=getAnimal(); a.makeSound(); Інтерфейси FlyBehavior або QuackBehavior разом із відповідними класами реалізують конкретну поведінку (рис. 2.9).
Рис. 2.9. Результат моделювання архітектури програмної системи
Така архітектура дозволяє використовувати поведінку польоту і крякання в других типах об’єктів, потому що поведінка не скривається в класах Duck. Крім того, можна додавати нові аспекти поведінки без зміни існуючих класів поведінки і без наслідків для класів Duck, що використовують існуючу поведінку. Поиск по сайту: |
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Студалл.Орг (0.005 сек.) |