Читати книгу - "Занурення в патерни проектування, Олександр Швець"
Шрифт:
Інтервал:
Додати в закладку:
Що таке хороший дизайн? За якими критеріями його оцінювати, і яких правил дотримуватися при розробці? Як забезпечити достатній рівень гнучкості, зв’язаності, керованості, стабільності та зрозумілості коду?
Все це правильні запитання, але для кожної програми відповідь буде трохи відрізнятися. Давайте розглянемо універсальні принципи проектування, які допоможуть вам формулювати відповіді на ці запитання самостійно.
До речі, більшість патернів, наведених у цій книзі, базується саме на перерахованих нижче принципах.
Інкапсулюйте те, що змінюєтьсяВизначте аспекти програми, класу або методу, які змінюються найчастіше, і відокремте їх від того, що залишається постійним.
Цей принцип має на меті зменшити наслідки, викликані змінами. Уявіть, що ваша програма — це корабель, а зміни — то підступні міни на його шляху. Натикаючись на міну, корабель заповнюється водою та тоне.
Знаючи це, ви можете розділити трюм корабля на незалежні секції, проходи між якими наглухо зачиняти. Тепер після зіткнення з міною корабель залишиться на плаву. Вода затопить лише одну секцію, залишивши решту без змін.
Ізолюючи мінливі частини програми в окремих модулях, класах або методах, ви зменшуєте кількість коду, якого торкнуться наступні зміни. Отже, вам потрібно буде витратити менше зусиль на те, щоб привести програму до робочого стану, налагодити та протестувати код, що змінився. Де менше роботи, там менша вартість розробки. А там, де менша вартість, там і перевага перед конкурентами.
Приклад інкапсуляції на рівні методуПрипустімо, що ви розробляєте інтернет-магазин. Десь всередині вашого коду знаходиться метод getOrderTotal, що розраховує фінальну суму замовлення з урахуванням розміру податку.
Ми можемо припустити, що код обчислення податків, імовірно, буде часто змінюватися. По-перше, логіка нарахування податку залежить від країни, штату й навіть міста, в якому знаходиться покупець. До того ж, розмір податку не сталий і може змінюватися з часом.
Через ці зміни вам доведеться постійно торкатися методу getOrderTotal, який, насправді, не особливо цікавиться деталями обчислення податків.
method getOrderTotal(order) istotal = 0
foreach item in order.lineItems
total += item.price * item.quantity
if (order.country == "US")
total += total * 0.07 // US sales tax
else if (order.country == "EU"):
total += total * 0.20 // European VAT
return total
ДО: правила обчислення податків змішані з основним кодом методу.
Ви можете перенести логіку обчислення податків в окремий метод, приховавши деталі від оригінального методу.
method getOrderTotal(order) istotal = 0
foreach item in order.lineItems
total += item.price * item.quantity
total += total * getTaxAmount(order.country)
return total
method getTaxAmount(country) is
if (country == "US")
return 0.07 // US sales tax
else if (country == "EU")
return 0.20 // European VAT
else
return 0
ПІСЛЯ: розмір податку можна отримати, викликавши один метод.
Тепер зміни податків будуть ізольовані в рамках одного методу. Більш того, якщо логіка обчислення податків ще більш ускладниться, вам буде легше отримати цей метод до власного класу.
Приклад інкапсуляції на рівні класуВидобути логіку податків до власного класу? Якщо логіка податків стала занадто складною, то чому б і ні?
ДО: обчислення податків у класі замовлень.
Об’єкти замовлень делегуватимуть обчислення податків окремому об’єкту-калькулятору податків.
ПІСЛЯ: обчислення податків приховано в класі замовлень.
Програмуйте на рівні інтерфейсуПрограмуйте на рівні інтерфейсу, а не на рівні реалізації. Код повинен залежати від абстракцій, а не від конкретних класів.
Гнучкість архітектури побудованої на класах виражається в тому, що їх можна легко розширювати, не ламаючи існуючий код. Для прикладу повернемося до класу котів. Клас Кіт, який їсть тільки сардельки, буде менш гнучким, ніж той, який може їсти будь-яку їжу. При цьому останнього можна буде годувати й сардельками теж, адже вони є їжею.
Коли вам потрібно налагодити взаємодію між двома об’єктами різних класів, то простіше всього зробити один клас прямо залежним від іншого. Що й казати, якщо, зазвичай, я й сам з цього починаю. Але є й інший, більш гнучкий спосіб.
Визначте, що саме потрібно одному об’єкту від іншого, які методи він викликає. Потім опишіть ці методи в окремому інтерфейсі. Зробіть так, щоб клас-залежність дотримувався цього інтерфейсу. Скоріше за все, потрібно буде лише додати цей інтерфейс до опису класу. Тепер ви можете зробити інший клас залежним від інтерфейсу, а не конкретного класу.До та після вилучення інтерфейсу.
Код праворуч більш гнучкий, але й більш складний від того коду, що ліворуч.
Увага!
Сайт зберігає кукі вашого браузера. Ви зможете в будь-який момент зробити закладку та продовжити читання книги «Занурення в патерни проектування, Олександр Швець», після закриття браузера.