Читати книгу - "Занурення в патерни проектування, Олександр Швець"
Шрифт:
Інтервал:
Додати в закладку:
Клас має мати лише один мотив для зміни.
Намагайтесь досягти того, щоб кожен клас відповідав тільки за одну частину функціональності програми, причому вона повинна бути повністю інкапсульована в цей клас (читай, прихована всередині класу).
Принцип єдиного обов’язку призначений для боротьби зі складністю. Коли у вашій програмі всього 200 рядків, то дизайн, як такий, взагалі не потрібен. Достатньо охайно написати 5-7 методів, і все буде добре. Проблеми виникають тоді, коли система росте та збільшується в масштабах. Коли клас розростається, він просто перестає вміщуватися в голові. Навігація ускладнюється, на очі потрапляють непотрібні деталі, пов’язані з іншим аспектом, в результаті кількість понять починає перевищувати мозковий стек, і ви втрачаєте контроль над кодом.
Якщо клас робить занадто багато речей одразу, вам доводиться змінювати його щоразу, коли одна з цих речей змінюється. При цьому є ризик пошкодження інших частин класу, яких ви навіть не планували торкатися.
Добре мати можливість зосередитися на складних аспектах системи окремо. Але, якщо вам складно це робити, застосовуйте принцип єдиного обов’язку, розділяючи ваші класи на частини.
ПрикладКлас Employee має відразу кілька причин для зміни. Перша пов’язана з головним завданням класу — керуванням даними співробітника. Але є й інша: зміни, пов’язані з форматуванням звіту для друку, зачіпатимуть клас співробітників.
ДО: клас співробітника містить різнорідні поведінки.
Проблему можна вирішити, виділивши операцію друку в окремий клас.
ПІСЛЯ: зайва поведінка переїхала до власного класу.
O Принцип відкритості/закритості Open/Closed PrincipleРозширюйте класи, але не змінюйте їхній початковий код.
Прагніть досягти того, щоб класи були відкритими для розширення, але закритими для зміни. Головна ідея цього принципу в тому, щоб не ламати існуючий код при внесенні змін до програми.
Клас можна назвати відкритим, якщо він доступний для розширення. Наприклад, у вас є можливість розширити набір операцій або додати до нього нові поля, створивши власний підклас.
У той же час, клас можна назвати закритим (а краще сказати закінченим), якщо він готовий до використання іншими класами — його інтерфейс вже остаточно визначено, і він не змінюватиметься в майбутньому.
Якщо клас уже був написаний, схвалений, протестований, можливо, внесений до бібліотеки і включений до проекту, не бажано намагатися модифікувати його вміст після цього. Замість цього ви можете створити підклас і розширити в ньому базову поведінку, не змінюючи код батьківського класу безпосередньо.
Але не варто дотримуватись цього принципу буквально для кожної зміни. Якщо вам потрібно виправити помилку в початковому класі, просто візьміть і зробіть це. Немає сенсу вирішувати проблему батька в дочірньому класі.
ПрикладКлас замовлень має метод розрахунку вартості доставки, причому способи доставки «зашиті» безпосередньо в сам метод. Якщо вам потрібно буде додати новий спосіб доставки — доведеться зачіпати весь клас Order.
ДО: код класу замовлення потрібно буде змінювати при додаванні нового способу доставки.
Проблему можна вирішити, якщо застосувати патерн Стратегія. Для цього потрібно виділити способи доставки у власні класи з загальним інтерфейсом.
ПІСЛЯ: нові способи доставки можна додати, не зачіпаючи клас замовлень.
Тепер при додаванні нового способу доставки потрібно буде реалізувати новий клас інтерфейсу доставки, не зачіпаючи класу замовлень. Об’єкт способу доставки до класу замовлення буде подавати клієнтський код, який раніше встановлював спосіб доставки простим рядком.
Бонус цього рішення в тому, що розрахунок часу та дати доставки теж можна помістити до нових класів, підкоряючись принципу єдиного обов’язку.
L Принцип підстановки Лісков Liskov Substitution Principle 7Підкласи повинні доповнювати, а не підміняти поведінку базового класу.
Намагайтесь створювати підкласи таким чином, щоб їхні об’єкти можна було б підставляти замість базового класу, не ламаючи при цьому функціональність клієнтського коду.
Принцип підстановки — це ряд перевірок, які допомагають передбачити, чи залишиться підклас сумісним з іншим кодом програми, який успішно працював до цього, використовуючи об’єкти базового класу. Особливо це важливо під час розробки бібліотек та фреймворків, коли ваші класи використовуються іншими людьми, а ви не зможете впливати на чужий клієнтський код, навіть якщо б захотіли.
На відміну від інших принципів, які визначено дуже вільно, і вони мають безліч трактувань, принцип підстановки має певні формальні вимоги до підкласів, а точніше, до методів, перевизначених в них.
Типи параметрів методу підкласу повинні збігатися або бути більш абстрактними, ніж типи параметрів базового методу. Звучить заплутано? Розглянемо, все на прикладі.
Базовий клас має метод feed(Cat c), який вміє годувати хатніх котів. Клієнтський код це знає і завжди передає до методу кота. Добре: Ви створили підклас і перевизначили метод годування так, щоб нагодувати будь-яку тварину: feed(Animal c). Якщо підставити цей клас у клієнтський код — нічого поганого не станеться. Клієнтський код подасть до методу кота, але метод вміє годувати всіх тварин, тому нагодує і кота. Погано: Ви створили інший підклас, в якому є метод, щоУвага!
Сайт зберігає кукі вашого браузера. Ви зможете в будь-який момент зробити закладку та продовжити читання книги «Занурення в патерни проектування, Олександр Швець», після закриття браузера.