feat(uk): translate refactor references (code-smells + catalog)

03-skills/refactor/references/code-smells.md (669 lines)
03-skills/refactor/references/refactoring-catalog.md (1023 lines)

P3 complete. Remaining: P4 root docs (5 files).

Ref: luongnv89/claude-howto#63
This commit is contained in:
Evgenij I
2026-04-09 23:08:46 +03:00
parent b56f1e111a
commit c0d400b21b
2 changed files with 1688 additions and 0 deletions
@@ -0,0 +1,669 @@
# Каталог запахів коду
Комплексний довідник запахів коду на основі книги Мартіна Фаулера *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*
File diff suppressed because it is too large Load Diff