1. Заміна прямих залежностей на обмін повідомленнями

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

У цьому випадку модулям не потрібно "знати один про одного", тобто містити прямі посилання і взаємодіяти безпосередньо, а достатньо лише обмінюватися повідомленнями (messages) або подіями (events).

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

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

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

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

Тому дуже важливо реалізовувати механізм повідомлень дуже грамотно. І тому є різні шаблони.

Спостерігач (Observer). Застосовується у разі залежності "один-багатьом", коли безліч модулів залежать від стану одного – основного. Використовує механізм розсилки, який полягає в тому, що основний модуль просто розсилає однакові повідомлення всім своїм передплатникам, а модулі, зацікавлені в цій інформації, реалізують інтерфейс “передплатника” і підписуються на розсилку.

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

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

Організація взаємодії у вигляді розсилки повідомлень має додатковий “бонус” — необов'язковість існування “передплатників” на “опубліковані” (такі, що розсилаються) повідомлення. Якісно спроєктована система допускає додавання/видалення модулів у будь-який час.

2. Шина обміну повідомленнями

Можна організувати обмін повідомленнями та по-іншому використовувати для цього патерн Посередник (Mediator).

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

Як результат взаємодії модулів один з одним (“всі з усіма”) замінюється на взаємодією модулів лише з посередником (“один із усіма”). Говорять, що посередник інкапсулює взаємодію між безліччю модулів.

Шина обміну повідомленнями

Це так званий розумний посередник. Саме там найчастіше розробники починають додавати свої “костилі”, і тим самим впливають на поведінку окремих модулів через увімкнення/вимкнення отримання певних повідомлень.

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

Важливо! Модулі можуть пересилати один одному не лише прості повідомлення, а й об'єкти-команди. Така взаємодія описується шаблоном Команда ( Command). Суть полягає в інкапсулюванні запиту виконання певної дії у вигляді окремого об'єкта.

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

3. Закон Деметри (Law of Demeter)

Закон Деметри забороняє використання неявних залежностей: "Об'єкт A не повинен мати можливість отримати безпосередній доступ до об'єкта C, якщо об'єкт A має доступ до об'єкта B, і об'єкт B має доступ до об'єкта C".

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

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

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

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

4. Композиція замість успадкування

Це дуже велика та цікава тема, і вона варта як мінімум окремої лекції. На цю тему в інтернеті зламали чимало списів, поки не досягли консенсусу — успадкування використовуємо по мінімуму, композицію — по максимуму.

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

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

Детальніше про патерни ми поговоримо трохи згодом.

undefined
3
Опрос
null,  14 уровень,  9 лекция
недоступен
null
Архітектура ПЗ, клієнт-серверна архітектура, MVC