Что такое DRY на самом деле? Частое заблуждение: DRY – это просто про копирование одинаковых строк кода. На самом деле, его суть гораздо глубже. Принцип DRY, сформулированный Энди Хантом и Дейвом Томасом в книге «The Pragmatic Programmer», гласит: «Каждое знание должно иметь единственное, непротиворечивое и авторитетное представление в рамках системы». Проще говоря, любая логика, правило, алгоритм или константа должны быть определены в программе в одном-единственном месте. Если вам нужно изменить эту логику, вы делаете это только в одном месте, а не бегаете по всему коду в поисках всех ее вхождений.
Почему дублирование – это зло? Конкретные последствия.
- Трудности поддержки и модификации (Maintainability). Представьте, что у вас в 15 разных местах кода разбросана формула расчета скидки. Вдруг бизнес меняет правило: скидка теперь зависит не только от суммы, но и от статуса клиента. Вам придется найти и исправить ВСЕ 15 мест. Вы почти наверняка что-то пропустите, что приведет к ошибкам и неконсистентному поведению системы.
- Увеличение количества багов. Исправляя ошибку в одном экземпляре логики, вы забываете исправить ее в другом, дублированном. Теперь в системе сосуществуют и правильная, и ошибочная версии одной и той же функции.
- Раздувание кодовой басы (Code Bloat). Код становится больше, чем необходимо. Это затрудняет его чтение, анализ и навигацию.
- Нарушение принципа единственной ответственности (Single Responsibility Principle, SRP). Если логика размазана по разным модулям, то за ее корректность отвечает не один, а множество компонентов.
* Магические числа: `if (user.age > 18) { ... }` и в другом файле `if (person.years > 18) { ... }`. Возраст совершеннолетия – это знание. Его нужно вынести в константу `const LEGAL_AGE = 18;` и использовать ее.
* Дублирование блоков кода: Два разных метода, которые оба подключаются к базе данных, выполняют один и тот же запрос для проверки существования пользователя. Этот запрос нужно вынести в отдельный метод, например, `userRepository.existsById(id)`.
* Копирование целых функций: Вы написали функцию `validateEmail()` для проверки email в форме регистрации. Потом скопировали ее почти без изменений в код формы обратной связи и в личный кабинет. При изменении стандартов валидации (например, добавлении новых разрешенных доменов) придется менять все три копии.
Как применять DRY правильно: практические приемы.
- Выделение функций (Methods/Functions). Самый базовый прием. Если вы видите один и тот же или очень похожий блок кода в двух и более местах – вынесите его в отдельную функцию. Дайте ей понятное имя, которое отражает ее цель.
- Использование циклов (Loops). Вместо повторения одной и той же операции для каждого элемента вручную используйте циклы `for`, `while` или методы итерации (`forEach`, `map`).
- Создание классов и использование наследования (OOP). Если несколько классов имеют общие свойства и методы, создайте базовый (родительский) класс и вынесите общую логику туда. Производные классы будут наследовать ее. Но будьте осторожны с глубокими иерархиями наследования – иногда композиция предпочтительнее.
- Параметризация. Сделайте вашу новую функцию гибкой с помощью параметров. Вместо двух почти одинаковых функций `calculateDiscountForBooks()` и `calculateDiscountForElectronics()` создайте одну `calculateDiscount(productType, price)`, где `productType` будет параметром.
- Конфигурация и константы. Все значения, которые могут меняться (настройки, URL-адреса, строковые константы, пороги), должны храниться в одном месте: в конфигурационных файлах (.env, config.yml), классах-константах или отдельных модулях.
Начинающие, узнав о DRY, часто впадают в крайность – пытаются устранить любое сходство в коде, даже если оно случайное. Это приводит к созданию излишне абстрактных, сложных для понимания конструкций.
* Случайное дублирование: Два фрагмента кода выглядят одинаково сейчас, но их бизнес-логика принципиально разная и, вероятно, будет меняться независимо в будущем. Связывать их в одну абстракцию – ошибка. Лучше подождать и посмотреть, действительно ли это одно и то же знание.
* Преждевременная оптимизация: Не выносите в отдельную функцию то, что используется всего один раз, «на всякий случай». Это усложняет чтение кода без реальной выгоды.
* Нарушение читаемости: Иногда небольшое, осознанное дублирование делает код гораздо более понятным и локальным. Если для понимания простого метода приходится прыгать по пяти разным файлам с абстракциями, цена устранения дублирования может быть слишком высока.
Правило трех. Хорошей эвристикой для начинающих является «Правило трех»: выносите код в отдельную функцию/модуль тогда, когда вы увидели его дублирование в третий раз. Первый раз вы просто пишете код. Второй раз, когда вам нужно то же самое, – вы задумываетесь. Третий раз – это сигнал к действию по рефакторингу.
Заключение. Принцип DRY – это не догма, а руководство к созданию элегантного и удобного в поддержке кода. Его цель – снижение сложности и минимизация точек отказа. Для начинающего разработчика важно научиться видеть истинное, смысловое дублирование, а не просто одинаковые строки, и понимать, когда объединение логики принесет пользу, а когда – создаст ненужную хрупкую абстракцию. Начните с малого: выносите константы и простые функции, и со временем вы разовьете чутье на чистый код, который будет благодарен вам за легкость своих будущих изменений.
Комментарии (10)