Запахи кодуКод із запашком (код з душком, код, що погано пахне англ. code smell) — термін, що позначає код з ознаками (запахами) проблем в системі. Був введений Кентом Беком[1] і використаний Мартіном Фаулером в його книзі «Рефакторинг. Поліпшення існуючого коду»[2], з тих пір активно використовується розробниками ПЗ[3]. Запахи коду — це ключові ознаки необхідності рефакторингу[4]. Існують запахи, специфічні як для парадигм програмування, так і для конкретних мов. Основною проблемою, з якою стикаються розробники при боротьбі з запахами коду, є те, що критерії своєчасності рефакторингу неможливо чітко формалізувати без апеляції до естетики і умовного почуття прекрасного. Запахи коду — це не набір чітких правил, а опис місць, на які потрібно звертати увагу при рефакторингу[5]. Вони легко виявляються, але при цьому не у всіх випадках свідчать про проблеми[1]. Код із запашком веде до розпаду коду, розробники повинні прагнути до усунення запашку шляхом застосування одноразового або багаторазового рефакторинга[6]. У процесі рефакторингу відбувається позбавлення від запахів коду, що забезпечує можливість подальшого розвитку додатків з тією ж або більшою швидкістю. Відсутність регулярного рефакторинга з плином часу здатна повністю паралізувати проект, тому запахи коду необхідно усувати на ранніх стадіях[4]. Існують інструменти пошуку та виправлення запахів коду[7], проте досвід показує, що ніякі системи показників не можуть змагатися з людською інтуїцією, основаною на інформації[8]. Часто глибша проблема, вказана запахом коду, може бути розкрита, коли код піддається короткому циклу зворотного зв'язку, де вона перетворена в невеликі, керовані кроки, і отримана конструкція перевіряється, щоб побачити будь-які запахи коду, які можуть вказувати на необхідність більшого рефакторингу. З точки зору програміста, якому доручено виконувати рефакторинг, запах коду це евристичний алгоритм, який вказує, коли відбувався рефакторинг і які конкретні методи рефакторингу використовували. Таким чином, запах коду це наслідок рефакторингу. Але чому це відбувається? Дослідження 2015 року з використанням автоматизованого аналізу півмільйона комітів і ручного обстеження 9,164 комітів встановило, що:
Запахи кодуЗагальні запахи об'єктно-орієнтованого кодуДублювання кодуДублювання коду — це використання однакових структур коду в декількох місцях. Об'єднання цих структур дозволить поліпшити програмний код[8]. Приклади дублювання і методи їх усунення:
Довгий методСеред об'єктних програм найдовше живуть програми з короткими методами. Чим довша процедура, тим важче її зрозуміти. Якщо у методу гарна назва, то не потрібно дивитися його тіло[5]. Слід дотримуватися евристичного правила: якщо відчувається необхідність щось прокоментувати, потрібно написати метод. Навіть один рядок має сенс виділити в метод, якщо він потребує роз'яснень[10].
Великий класКоли клас реалізує занадто велику функціональність, варто подумати про винесення деякої частини коду в підклас. Це позбавить розробників від надмірної кількості наявних у класу атрибутів і дублювання коду[10].
Довгий список параметрівУ довгих списках параметрів важко розбиратися, вони стають суперечливими і складними у використанні. Використання об'єктів дозволяє, в разі зміни переданих даних, модифікувати тільки сам об'єкт. Працюючи з об'єктами, слід передавати рівно стільки, щоб метод міг отримати необхідні йому дані[11].
Розбіжні модифікаціїПроблема виникає, коли при модифікації в системі неможливо виділити певне місце, яке потрібно змінити. Це є наслідком поганої структурованості ПЗ або програмування методом копіювання-вставлення.
Стрільба дробомПри виконанні будь-яких модифікацій доводиться вносити безліч дрібних змін у велике число класів. «Стрільба дробом» схожа на «Розбіжну модифікацію», але є її протилежністю. «Розбіжна модифікація» має сенс, коли є один клас, в якому проводиться багато різних змін, а «Стрільба дробом» — це одна зміна, що зачіпає багато класів[12].
Заздрісні функціїМетод звертається до даних іншого об'єкта частіше, ніж до власних даних[5].
Фундаментальне практичне правило говорить: «Те, що змінюється одночасно, треба зберігати в одному місці». Дані та функції, що використовують ці дані, які зазвичай змінюються разом, але бувають винятки[13]. Групи данихГрупи даних, що зустрічаються спільно, потрібно перетворювати в самостійний клас[13].
Хороша перевірка: видалити одне із значень даних і перевірити, чи збереже сенс решта. Якщо ні, то це вірна ознака того, що дані напрошуються на об'єднання їх в об'єкт[13]. Одержимість елементарними типамиПроблема пов'язана з використанням елементарних типів замість маленьких об'єктів для невеликих завдань, таких як валюта, діапазони, спеціальні рядки для телефонних номерів тощо
Оператори типу switchОдним з очевидних ознак об'єктно-орієнтованого коду служить порівняно рідкісне використання операторів типу switch (або case). Часто один і той же блок switch виявляється розкиданим по різних місцях програми. При додаванні в перемикач нового варіанту доводиться шукати всі ці блоки switch і модифікувати їх. Як правило, помітивши блок switch, слід подумати про поліморфізм[15].
Паралельні ієрархії успадкуванняУ коді з таким запашком щоразу при породженні підкласу, одного з класів, доводиться створювати підклас іншого класу[15].
Лінивий класКлас, витрати на існування якого не окупаються виконуваними ним функціями, повинен бути ліквідований[15].
Теоретична спільністьЦей випадок виникає, коли на певному етапі існування програми забезпечується набір механізмів, який, можливо, буде потрібен для деякої майбутньої функціональності. У підсумку програму стає важче розуміти і супроводжувати[16].
Тимчасове полеТимчасові поля — це поля, які потрібні об'єкту лише за певних обставин. Такий стан речей важкий для розуміння, так як очікується, що об'єкту потрібні всі його поля[17].
Ланцюжок викликівЛанцюжок викликів з'являється тоді, коли клієнт запитує у одного об'єкта інший об'єкт, інший об'єкт запитує ще один об'єкт і т. д. Такі послідовності викликів означають, що клієнт пов'язаний з навігацією за структурою класів. Будь-які зміни проміжних зв'язків означають необхідність модифікації клієнта[16].
ПосередникНадмірне використання делегування може призвести до появи класів, у яких більшість методів складаються тільки з виклику методу іншого класу[16].
Недоречна близькість«Недоречна близькість» виникає тоді, коли класи частіше, ніж варто було б, занурені в закриті частини один одного[18].
Альтернативні класи з різними інтерфейсамиДва класи, в яких частина функціональності загальна, але методи, що реалізують її, мають різні параметри[19].
Неповнота бібліотечного класуБібліотеки через деякий час перестають задовольняти вимогам користувачів. Природне рішення — змінити дещо в бібліотеках, але бібліотечні класи не змінювати. Слід використовувати методи рефакторінга, спеціально призначені для цієї мети[19].
Класи данихКласи даних — це класи, які містять тільки поля і методи для доступу до них, це просто контейнери для даних, що використовуються іншими класами[19].
Відмова від наслідуванняЯкщо спадкоємець використовує лише малу частину успадкованих методів і властивостей батька — це є ознакою неправильної ієрархії.
КоментаріЧасто коментарі грають роль «дезодоранту» коду, який з'являється в ньому лише тому, що код поганий. Відчувши потребу написати коментар, спробуйте змінити структуру коду так, щоб будь-які коментарі стали зайвими[20].
Див. такожПримітки
Література
Посилання
|