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:
669
uk/03-skills/refactor/references/code-smells.md
Normal file
669
uk/03-skills/refactor/references/code-smells.md
Normal file
@@ -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*
|
||||||
1019
uk/03-skills/refactor/references/refactoring-catalog.md
Normal file
1019
uk/03-skills/refactor/references/refactoring-catalog.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user