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

Комплексний довідник запахів коду на основі книги Мартіна Фаулера *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*
