Антипаттерны
Magic Numbers
Magic Numbers — это антипаттерн, при котором в коде используются необъяснимые числовые константы без комментариев или именованных констант. Это делает код трудным для понимания и поддержки.
Magic Numbers — это числовые константы, которые:
- Не имеют объяснения — непонятно, что означает число
- Жестко закодированы — встроены прямо в код
- Не имеют контекста — непонятно, откуда взялось значение
- Сложно изменять — нужно искать по всему коду
- Легко ошибиться — можно использовать неправильное значение
Признаки Magic Numbers
Симптомы:
- Числа без объяснения — 42, 100, 3.14159 в коде
- Повторяющиеся числа — одно и то же число в разных местах
- Сложные вычисления — математические формулы с числами
- Пороговые значения — границы и лимиты
- Коды состояний — числовые коды ошибок
Примеры:
- Пороги:
if (price > 1000)— что означает 1000? - Таймауты:
setTimeout(callback, 5000)— 5 секунд? - Размеры:
buffer = new Array(1024)— 1KB? - Коды:
if (status === 200)— HTTP код?
Примеры Magic Numbers
Плохой пример
function calculateDiscount(price) {
if (price > 1000) {
return price * 0.1; // Что означает 0.1?
}
if (price > 500) {
return price * 0.05; // Что означает 0.05?
}
return 0;
}
function validateUser(user) {
if (user.age < 18) {
return false; // Почему 18?
}
if (user.password.length < 8) {
return false; // Почему 8?
}
return true;
}
function processData(data) {
const maxRetries = 3; // Почему 3?
const timeout = 5000; // Почему 5000?
for (let i = 0; i < maxRetries; i++) {
try {
return fetchData(data, timeout);
} catch (error) {
if (i === maxRetries - 1) {
throw error;
}
await sleep(1000); // Почему 1000?
}
}
}
function formatCurrency(amount) {
return amount.toFixed(2); // Почему 2?
}
function checkConnection() {
const maxAttempts = 5; // Почему 5?
const delay = 2000; // Почему 2000?
for (let i = 0; i < maxAttempts; i++) {
if (isConnected()) {
return true;
}
sleep(delay);
}
return false;
}
Хороший пример
// Именованные константы
const DISCOUNT_THRESHOLDS = {
HIGH: 1000, // Высокий порог для скидки
MEDIUM: 500 // Средний порог для скидки
};
const DISCOUNT_RATES = {
HIGH: 0.1, // 10% скидка для высокого порога
MEDIUM: 0.05 // 5% скидка для среднего порога
};
const VALIDATION_RULES = {
MIN_AGE: 18, // Минимальный возраст пользователя
MIN_PASSWORD_LENGTH: 8 // Минимальная длина пароля
};
const RETRY_CONFIG = {
MAX_RETRIES: 3, // Максимальное количество попыток
TIMEOUT_MS: 5000, // Таймаут в миллисекундах
RETRY_DELAY_MS: 1000 // Задержка между попытками
};
const CURRENCY_CONFIG = {
DECIMAL_PLACES: 2 // Количество знаков после запятой
};
const CONNECTION_CONFIG = {
MAX_ATTEMPTS: 5, // Максимальное количество попыток подключения
RETRY_DELAY_MS: 2000 // Задержка между попытками подключения
};
function calculateDiscount(price) {
if (price > DISCOUNT_THRESHOLDS.HIGH) {
return price * DISCOUNT_RATES.HIGH;
}
if (price > DISCOUNT_THRESHOLDS.MEDIUM) {
return price * DISCOUNT_RATES.MEDIUM;
}
return 0;
}
function validateUser(user) {
if (user.age < VALIDATION_RULES.MIN_AGE) {
return false;
}
if (user.password.length < VALIDATION_RULES.MIN_PASSWORD_LENGTH) {
return false;
}
return true;
}
function processData(data) {
for (let i = 0; i < RETRY_CONFIG.MAX_RETRIES; i++) {
try {
return fetchData(data, RETRY_CONFIG.TIMEOUT_MS);
} catch (error) {
if (i === RETRY_CONFIG.MAX_RETRIES - 1) {
throw error;
}
await sleep(RETRY_CONFIG.RETRY_DELAY_MS);
}
}
}
function formatCurrency(amount) {
return amount.toFixed(CURRENCY_CONFIG.DECIMAL_PLACES);
}
function checkConnection() {
for (let i = 0; i < CONNECTION_CONFIG.MAX_ATTEMPTS; i++) {
if (isConnected()) {
return true;
}
sleep(CONNECTION_CONFIG.RETRY_DELAY_MS);
}
return false;
}
Как исправить Magic Numbers?
Стратегии исправления:
1. Extract Constant (Извлечение константы)
Создать именованную константу для каждого магического числа.
2. Group Related Constants (Группировка констант)
Объединить связанные константы в объекты или перечисления.
3. Add Comments (Добавление комментариев)
Объяснить значение числа в комментарии.
4. Use Configuration (Использование конфигурации)
Вынести константы в конфигурационные файлы.
5. Create Enums (Создание перечислений)
Использовать перечисления для кодов состояний.
План рефакторинга:
- Поиск — найти все магические числа в коде
- Анализ — понять назначение каждого числа
- Группировка — объединить связанные константы
- Именование — дать понятные имена константам
- Замена — заменить числа на именованные константы
- Тестирование — убедиться, что функциональность сохранена
Профилактика Magic Numbers
Меры предотвращения:
- Именованные константы — всегда использовать константы вместо чисел
- Комментарии — объяснять значение каждого числа
- Конфигурация — выносить константы в конфигурационные файлы
- Code Review — проверять наличие магических чисел
- Статические анализаторы — использовать инструменты для поиска
Правила:
- Все числа > 0 должны быть именованными константами
- Все числа < 0 должны быть именованными константами
- Все дроби должны быть именованными константами
- Все коды состояний должны быть перечислениями
Преимущества исправления
- Понятность — код легче читать и понимать
- Поддерживаемость — легко изменять значения
- Безопасность — меньше ошибок при изменении
- Документированность — константы служат документацией
- Переиспользование — константы можно использовать повторно
Недостатки Magic Numbers
- Непонятность — сложно понять назначение числа
- Ошибки — легко использовать неправильное значение
- Сложность изменений — нужно искать по всему коду
- Отсутствие документации — нет объяснения значения
- Дублирование — одно число в разных местах
Помните: каждое число в коде должно иметь имя и объяснение!