Skip to content

Каталог запахів коду

Комплексний довідник запахів коду на основі книги Мартіна Фаулера Refactoring (2-ге видання). Запахи коду — це симптоми глибших проблем; вони вказують на те, що щось може бути не так з дизайном вашого коду.

«Запах коду — це поверхнева ознака, яка зазвичай відповідає глибшій проблемі в системі.» — Мартін Фаулер


Роздуті елементи (Bloaters)

Запахи коду, що представляють щось, що розрослося занадто великим для ефективної роботи.

Long Method (Довгий метод)

Ознаки:

  • Метод перевищує 30-50 рядків
  • Потрібно прокручувати, щоб побачити весь метод
  • Кілька рівнів вкладеності
  • Коментарі пояснюють, що роблять секції

Чому це погано:

  • Важко зрозуміти
  • Складно тестувати ізольовано
  • Зміни мають непередбачувані наслідки
  • Дубльована логіка ховається всередині

Рефакторинги:

  • Extract Method
  • Replace Temp with Query
  • Introduce Parameter Object
  • Replace Method with Method Object
  • Decompose Conditional

Приклад (до):

javascript
function processOrder(order) {
  // Валідація замовлення (20 рядків)
  if (!order.items) throw new Error('No items');
  if (order.items.length === 0) throw new Error('Empty order');
  // ... ще валідація

  // Обчислення підсумків (30 рядків)
  let subtotal = 0;
  for (const item of order.items) {
    subtotal += item.price * item.quantity;
  }
  // ... податок, доставка, знижки

  // Надсилання сповіщень (20 рядків)
  // ... логіка email
}

Приклад (після):

javascript
function processOrder(order) {
  validateOrder(order);
  const totals = calculateOrderTotals(order);
  sendOrderNotifications(order, totals);
  return { order, totals };
}

Large Class (Великий клас)

Ознаки:

  • Клас має багато полів екземпляра (>7-10)
  • Клас має багато методів (>15-20)
  • Назва класу розмита (Manager, Handler, Processor)
  • Методи не використовують усі поля екземпляра

Чому це погано:

  • Порушує принцип єдиної відповідальності
  • Важко тестувати
  • Зміни поширюються на неповʼязані функції
  • Складно повторно використовувати частини

Рефакторинги:

  • Extract Class
  • Extract Subclass
  • Extract Interface

Виявлення:

Рядків коду > 300
Кількість методів > 15
Кількість полів > 10

Primitive Obsession (Одержимість примітивами)

Ознаки:

  • Використання примітивів для доменних концепцій (string для email, int для грошей)
  • Масиви примітивів замість обʼєктів
  • Рядкові константи для кодів типів
  • Магічні числа/рядки

Чому це погано:

  • Немає валідації на рівні типу
  • Логіка розкидана по кодовій базі
  • Легко передати неправильні значення
  • Відсутні доменні концепції

Рефакторинги:

  • Replace Primitive with Object
  • Replace Type Code with Class
  • Replace Type Code with Subclasses
  • Replace Type Code with State/Strategy

Приклад (до):

javascript
const user = {
  email: 'john@example.com',     // Просто рядок
  phone: '1234567890',           // Просто рядок
  status: 'active',              // Магічний рядок
  balance: 10050                 // Копійки як ціле число
};

Приклад (після):

javascript
const user = {
  email: new Email('john@example.com'),
  phone: new PhoneNumber('1234567890'),
  status: UserStatus.ACTIVE,
  balance: Money.cents(10050)
};

Long Parameter List (Довгий список параметрів)

Ознаки:

  • Методи з 4+ параметрами
  • Параметри, що завжди зʼявляються разом
  • Булеві прапорці, що змінюють поведінку методу
  • Часте передавання null/undefined

Чому це погано:

  • Важко викликати правильно
  • Плутанина з порядком параметрів
  • Вказує, що метод робить занадто багато
  • Важко додавати нові параметри

Рефакторинги:

  • Introduce Parameter Object
  • Preserve Whole Object
  • Replace Parameter with Method Call
  • Remove Flag Argument

Приклад (до):

javascript
function createUser(firstName, lastName, email, phone,
                    street, city, state, zip,
                    isAdmin, isActive, createdBy) {
  // ...
}

Приклад (після):

javascript
function createUser(personalInfo, address, options) {
  // personalInfo: { firstName, lastName, email, phone }
  // address: { street, city, state, zip }
  // options: { isAdmin, isActive, createdBy }
}

Data Clumps (Групи даних)

Ознаки:

  • Ті самі 3+ поля зʼявляються разом повторно
  • Параметри, що завжди подорожують разом
  • Класи з підмножинами полів, що належать один одному

Чому це погано:

  • Дубльована логіка обробки
  • Відсутня абстракція
  • Важче розширювати
  • Вказує на прихований клас

Рефакторинги:

  • Extract Class
  • Introduce Parameter Object
  • Preserve Whole Object

Приклад:

javascript
// Група даних: координати (x, y, z)
function movePoint(x, y, z, dx, dy, dz) { }
function scalePoint(x, y, z, factor) { }
function distanceBetween(x1, y1, z1, x2, y2, z2) { }

// Витяг класу Point3D
class Point3D {
  constructor(x, y, z) { }
  move(delta) { }
  scale(factor) { }
  distanceTo(other) { }
}

Зловживання обʼєктною орієнтацією (OO Abusers)

Запахи, що вказують на неповне або некоректне використання принципів ООП.

Switch Statements (Оператори Switch)

Ознаки:

  • Довгі ланцюжки switch/case або if/else
  • Той самий switch у кількох місцях
  • Switch за кодами типів
  • Додавання нових випадків вимагає змін всюди

Чому це погано:

  • Порушує принцип відкритості/закритості
  • Зміни поширюються на всі місця з switch
  • Важко розширювати
  • Часто вказує на відсутній поліморфізм

Рефакторинги:

  • Replace Conditional with Polymorphism
  • Replace Type Code with Subclasses
  • Replace Type Code with State/Strategy

Приклад (до):

javascript
function calculatePay(employee) {
  switch (employee.type) {
    case 'hourly':
      return employee.hours * employee.rate;
    case 'salaried':
      return employee.salary / 12;
    case 'commissioned':
      return employee.sales * employee.commission;
  }
}

Приклад (після):

javascript
class HourlyEmployee {
  calculatePay() {
    return this.hours * this.rate;
  }
}

class SalariedEmployee {
  calculatePay() {
    return this.salary / 12;
  }
}

Temporary Field (Тимчасове поле)

Ознаки:

  • Поля екземпляра, що використовуються лише в деяких методах
  • Поля, що встановлюються умовно
  • Складна ініціалізація для певних випадків

Чому це погано:

  • Плутанина — поле існує, але може бути null
  • Важко зрозуміти стан обʼєкта
  • Вказує на приховану умовну логіку

Рефакторинги:

  • Extract Class
  • Introduce Null Object
  • Replace Temp Field with Local

Refused Bequest (Відхилена спадщина)

Ознаки:

  • Підклас не використовує успадковані методи/дані
  • Підклас перевизначає, щоб нічого не робити
  • Успадкування використовується для повторного використання коду, а не для відношення IS-A

Чому це погано:

  • Неправильна абстракція
  • Порушує принцип підстановки Лісков
  • Оманлива ієрархія

Рефакторинги:

  • Push Down Method/Field
  • Replace Subclass with Delegate
  • Replace Inheritance with Delegation

Alternative Classes with Different Interfaces (Альтернативні класи з різними інтерфейсами)

Ознаки:

  • Два класи, що роблять схожі речі
  • Різні назви методів для тієї самої концепції
  • Можуть використовуватися взаємозамінно

Чому це погано:

  • Дубльовані реалізації
  • Немає спільного інтерфейсу
  • Важко перемикатися між ними

Рефакторинги:

  • Rename Method
  • Move Method
  • Extract Superclass
  • Extract Interface

Перешкоди для змін (Change Preventers)

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

Divergent Change (Дивергентна зміна)

Ознаки:

  • Один клас змінюється з кількох різних причин
  • Зміни в різних областях запускають редагування того самого класу
  • Клас є «God-класом»

Чому це погано:

  • Порушує принцип єдиної відповідальності
  • Висока частота змін
  • Конфлікти злиття

Рефакторинги:

  • Extract Class
  • Extract Superclass
  • Extract Subclass

Приклад: Клас User змінюється через:

  • Зміни автентифікації
  • Зміни профілю
  • Зміни білінгу
  • Зміни сповіщень

→ Витягти: AuthService, ProfileService, BillingService, NotificationService


Shotgun Surgery (Хірургія дробовиком)

Ознаки:

  • Одна зміна вимагає редагування в багатьох класах
  • Мала функція потребує правок у 10+ файлах
  • Зміни розкидані, важко знайти всі

Чому це погано:

  • Легко пропустити місце
  • Високий звʼязок (coupling)
  • Зміни схильні до помилок

Рефакторинги:

  • Move Method
  • Move Field
  • Inline Class

Виявлення: Шукайте: додавання одного поля вимагає змін у >5 файлах.


Parallel Inheritance Hierarchies (Паралельні ієрархії успадкування)

Ознаки:

  • Створення підкласу в одній ієрархії вимагає підкласу в іншій
  • Префікси класів збігаються (напр., DatabaseOrder, DatabaseProduct)

Чому це погано:

  • Подвійне обслуговування
  • Звʼязок між ієрархіями
  • Легко забути одну сторону

Рефакторинги:

  • Move Method
  • Move Field
  • Усунути одну ієрархію

Непотрібне (Dispensables)

Щось непотрібне, що слід видалити.

Comments (Надмірні коментарі)

Ознаки:

  • Коментарі, що пояснюють, що робить код
  • Закоментований код
  • TODO/FIXME, що залишаються назавжди
  • Вибачення в коментарях

Чому це погано:

  • Коментарі брешуть (розсинхронізуються)
  • Код має бути самодокументованим
  • Мертвий код створює плутанину

Рефакторинги:

  • Extract Method (назва пояснює що)
  • Rename (ясність без коментарів)
  • Видалити закоментований код
  • Introduce Assertion

Хороші vs погані коментарі:

javascript
// ПОГАНО: Пояснює що
// Цикл по користувачах і перевірка чи активні
for (const user of users) {
  if (user.status === 'active') { }
}

// ДОБРЕ: Пояснює чому
// Лише активні — неактивні обробляються задачею очищення
const activeUsers = users.filter(u => u.isActive);

Duplicate Code (Дубльований код)

Ознаки:

  • Той самий код у кількох місцях
  • Схожий код з малими варіаціями
  • Патерни копі-пасту

Чому це погано:

  • Виправлення помилок потрібне в кількох місцях
  • Ризик неконсистентності
  • Роздута кодова база

Рефакторинги:

  • Extract Method
  • Extract Class
  • Pull Up Method (в ієрархіях)
  • Form Template Method

Правило виявлення: Будь-який код, дубльований 3+ разів, слід витягти.


Lazy Class (Ледачий клас)

Ознаки:

  • Клас не робить достатньо для виправдання свого існування
  • Обгортка без доданої цінності
  • Результат надмірної інженерії

Чому це погано:

  • Накладні витрати обслуговування
  • Непотрібна непрямість
  • Складність без користі

Рефакторинги:

  • Inline Class
  • Collapse Hierarchy

Dead Code (Мертвий код)

Ознаки:

  • Недосяжний код
  • Невикористані змінні/методи/класи
  • Закоментований код
  • Код за неможливими умовами

Чому це погано:

  • Плутанина
  • Тягар обслуговування
  • Уповільнює розуміння

Рефакторинги:

  • Remove Dead Code
  • Safe Delete

Виявлення:

bash
# Шукати невикористані експорти
# Шукати невикористані функції
# Попередження IDE "unused"

Speculative Generality (Спекулятивна загальність)

Ознаки:

  • Абстрактні класи з одним підкласом
  • Невикористані параметри «на майбутнє»
  • Методи, що лише делегують
  • «Фреймворк» для одного випадку

Чому це погано:

  • Складність без користі
  • YAGNI (You Ain't Gonna Need It — Вам це не знадобиться)
  • Важче зрозуміти

Рефакторинги:

  • Collapse Hierarchy
  • Inline Class
  • Remove Parameter
  • Rename Method

Звʼязувачі (Couplers)

Запахи, що представляють надмірний звʼязок між класами.

Feature Envy (Заздрість до функцій)

Ознаки:

  • Метод використовує більше даних іншого класу, ніж свого
  • Багато викликів геттерів іншого обʼєкта
  • Дані та поведінка розділені

Чому це погано:

  • Неправильне розташування поведінки
  • Погана інкапсуляція
  • Важко супроводжувати

Рефакторинги:

  • Move Method
  • Move Field
  • Extract Method (потім перемістити)

Приклад (до):

javascript
class Order {
  getDiscountedPrice(customer) {
    // Інтенсивно використовує дані customer
    if (customer.loyaltyYears > 5) {
      return this.price * customer.discountRate;
    }
    return this.price;
  }
}

Приклад (після):

javascript
class Customer {
  getDiscountedPriceFor(price) {
    if (this.loyaltyYears > 5) {
      return price * this.discountRate;
    }
    return price;
  }
}

Inappropriate Intimacy (Недоречна інтимність)

Ознаки:

  • Класи отримують доступ до приватних частин один одного
  • Двосторонні посилання
  • Підкласи знають занадто багато про батьків

Чому це погано:

  • Високий звʼязок
  • Зміни каскадуються
  • Важко модифікувати один без іншого

Рефакторинги:

  • Move Method
  • Move Field
  • Change Bidirectional to Unidirectional
  • Extract Class
  • Hide Delegate

Message Chains (Ланцюжки повідомлень)

Ознаки:

  • Довгі ланцюжки викликів: a.getB().getC().getD().getValue()
  • Клієнт залежить від структури навігації
  • Код типу «потяг-катастрофа» (train wreck)

Чому це погано:

  • Крихкий — будь-яка зміна ламає ланцюжок
  • Порушує Закон Деметри
  • Звʼязок зі структурою

Рефакторинги:

  • Hide Delegate
  • Extract Method
  • Move Method

Приклад:

javascript
// Погано: Ланцюжок повідомлень
const managerName = employee.getDepartment().getManager().getName();

// Краще: Приховати делегування
const managerName = employee.getManagerName();

Middle Man (Посередник)

Ознаки:

  • Клас, що лише делегує іншому
  • Половина методів — делегування
  • Немає доданої цінності

Чому це погано:

  • Непотрібна непрямість
  • Накладні витрати обслуговування
  • Заплутана архітектура

Рефакторинги:

  • Remove Middle Man
  • Inline Method

Посібник серйозності запахів

СерйозністьОписДія
КритичнийБлокує розробку, спричиняє помилкиВиправити негайно
ВисокийЗначний тягар обслуговуванняВиправити в поточному спринті
СереднійПомітний, але керованийЗапланувати на найближче майбутнє
НизькийНезначна незручністьВиправляти при нагоді

Контрольний список швидкого виявлення

Використовуйте цей список при скануванні коду:

  • Є методи > 30 рядків?
  • Є класи > 300 рядків?
  • Є методи з > 4 параметрами?
  • Є дубльовані блоки коду?
  • Є switch/case за кодами типів?
  • Є невикористаний код?
  • Є методи, що інтенсивно використовують дані іншого класу?
  • Є довгі ланцюжки викликів?
  • Є коментарі, що пояснюють «що», а не «чому»?
  • Є примітиви, що мають бути обʼєктами?

Додаткове читання

  • Fowler, M. (2018). Refactoring: Improving the Design of Existing Code (2nd ed.)
  • Kerievsky, J. (2004). Refactoring to Patterns
  • Feathers, M. (2004). Working Effectively with Legacy Code