Lapa Knowledge Base
Антипаттерны

Magic Numbers

Magic Numbers — это антипаттерн, при котором в коде используются необъяснимые числовые константы без комментариев или именованных констант. Это делает код трудным для понимания и поддержки.

Magic Numbers — это числовые константы, которые:

  • Не имеют объяснения — непонятно, что означает число
  • Жестко закодированы — встроены прямо в код
  • Не имеют контекста — непонятно, откуда взялось значение
  • Сложно изменять — нужно искать по всему коду
  • Легко ошибиться — можно использовать неправильное значение

Признаки Magic Numbers

Симптомы:

  1. Числа без объяснения — 42, 100, 3.14159 в коде
  2. Повторяющиеся числа — одно и то же число в разных местах
  3. Сложные вычисления — математические формулы с числами
  4. Пороговые значения — границы и лимиты
  5. Коды состояний — числовые коды ошибок

Примеры:

  • Пороги: 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 (Извлечение константы)

Создать именованную константу для каждого магического числа.

Объединить связанные константы в объекты или перечисления.

3. Add Comments (Добавление комментариев)

Объяснить значение числа в комментарии.

4. Use Configuration (Использование конфигурации)

Вынести константы в конфигурационные файлы.

5. Create Enums (Создание перечислений)

Использовать перечисления для кодов состояний.

План рефакторинга:

  1. Поиск — найти все магические числа в коде
  2. Анализ — понять назначение каждого числа
  3. Группировка — объединить связанные константы
  4. Именование — дать понятные имена константам
  5. Замена — заменить числа на именованные константы
  6. Тестирование — убедиться, что функциональность сохранена

Профилактика Magic Numbers

Меры предотвращения:

  1. Именованные константы — всегда использовать константы вместо чисел
  2. Комментарии — объяснять значение каждого числа
  3. Конфигурация — выносить константы в конфигурационные файлы
  4. Code Review — проверять наличие магических чисел
  5. Статические анализаторы — использовать инструменты для поиска

Правила:

  • Все числа > 0 должны быть именованными константами
  • Все числа < 0 должны быть именованными константами
  • Все дроби должны быть именованными константами
  • Все коды состояний должны быть перечислениями

Преимущества исправления

  • Понятность — код легче читать и понимать
  • Поддерживаемость — легко изменять значения
  • Безопасность — меньше ошибок при изменении
  • Документированность — константы служат документацией
  • Переиспользование — константы можно использовать повторно

Недостатки Magic Numbers

  • Непонятность — сложно понять назначение числа
  • Ошибки — легко использовать неправильное значение
  • Сложность изменений — нужно искать по всему коду
  • Отсутствие документации — нет объяснения значения
  • Дублирование — одно число в разных местах

Помните: каждое число в коде должно иметь имя и объяснение!

Copyright © 2026