Защита принципа DRY: Секреты и советы от мастеров разработки

Статья раскрывает тонкости применения принципа DRY (Don't Repeat Yourself) на основе советов опытных разработчиков. Рассматривается различие между дублированием кода и знания, правило трех, преимущества композиции над наследованием, случаи стратегического дублирования, использование инструментов и опасность излишних абстракций. Цель — научиться применять DRY разумно для создания гибкого и поддерживаемого кода.
Принцип DRY (Don't Repeat Yourself — «Не повторяйся») — один из краеугольных камней качественного программного обеспечения, сформулированный в книге «The Pragmatic Programmer». Его суть проста: каждая часть знания должна иметь единственное, непротиворечивое и авторитетное представление в системе. Однако слепое следование DRY без понимания контекста может привести к хрупким абстракциям, чрезмерно связанному коду и затрудненному поддержанию. Мастера разработки знают, что защищать DRY нужно умно. Давайте разберем их секреты.

Первый и главный секрет: различайте дублирование кода и дублирование знания. Прямое копирование нескольких строк — это очевидное дублирование кода. Но иногда две функции могут выглядеть по-разному, но реализовывать одно и то же бизнес-правило (знание). Например, проверка формата email в форме регистрации и в форме восстановления парода — это одно знание. Его изменение в одном месте должно повлечь изменение в другом. А вот две функции, которые совершают HTTP-запрос, но к разным API с разными контрактами и причинами для изменения — это разное знание, даже если код запроса выглядит идентично. Их объединение создаст нежелательную связь.

Мастера советуют применять «правило трех». Не абстрагируйте дублирование при первом же его появлении. Дождитесь, когда один и тот же паттерн повторится трижды. Это дает время понять, действительно ли это одно и то же знание, и уловить его стабильную структуру. Преждевременная абстракция, основанная на двух примерах, часто оказывается ошибочной и требует болезненного рефакторинга.

Рассмотрим пример. У вас есть два обработчика, которые логируют начало и конец операции.
```elixir
# Первое появление
def process_order(order) do
 Logger.info("Order processing started: #{order.id}")
 # ... логика
 Logger.info("Order processing finished: #{order.id}")
end

# Второе появление
def generate_report(data) do
 Logger.info("Report generation started for #{data.period}")
 # ... логика
 Logger.info("Report generation finished for #{data.period}")
end
```
После третьего похожего случая можно создать абстракцию:
```elixir
defmodule Instrumentation do
 def with_logging(operation_name, identifier, func) do
 Logger.info("#{operation_name} started: #{identifier}")
 result = func.()
 Logger.info("#{operation_name} finished: #{identifier}")
 result
 end
end

# Использование
def process_order(order) do
 Instrumentation.with_logging("Order processing", order.id, fn ->
 # ... логика
 end)
end
```
Это удачная абстракция, так как она инкапсулирует знание о формате логирования операций.

Второй совет: используйте композицию, а не наследование, для борьбы с дублированием. Наследование, особенно глубокое, создает жесткие иерархии, где изменение родительского класса может сломать множество непредсказуемых мест. Композиция (включение одного объекта в другой) или миксины (в Elixir) дают большую гибкость. Вместо того чтобы создавать базовый класс `BaseController` со всеми возможными методами, выделите отдельные модули-поведения (behaviours) и подключайте их там, где нужно.

Третий секрет — стратегическое дублирование. Бывают случаи, когда дублирование является меньшим злом. Например, при разработке микросервисов. Дублирование некоторой модели данных или библиотеки утилит в двух сервисах может быть оправдано, если это позволяет сервисам развиваться независимо и не создает единую точку отказа. Дублирование здесь защищает от сильной связности.

Четвертый аспект — инструментарий. Современные IDE и линтеры — ваши союзники в защите DRY. Настраивайте линтеры (например, `Credo` в Elixir) для поиска дублирования кода. Но не слепо доверяйте метрикам. Линтер может указать на два похожих блока, но решение об их объединении должно приниматься человеком на основе семантики, а не только синтаксиса.

Работа с конфигурациями и константами — классическая область применения DRY. Вынесение всех строковых литералов, кодов ошибок, URL-адресов в конфигурационные файлы или модули констант — это must. В Phoenix это `config/*.exs` файлы и модули типа `MyApp.Web.Endpoint`.

Опасность, о которой предупреждают мастера, — это создание «абстрактного фабричного генератора адаптеров моста». Слишком умные и обобщенные абстракции, предназначенные для покрытия всех гипотетических будущих случаев, становятся кошмаром для понимания и поддержки. Код должен быть настолько абстрактным, насколько это необходимо для решения текущих задач, и не более. Простота и ясность часто важнее полного устранения дублирования.

Наконец, защита DRY — это командная работа. Проводите регулярные код-ревью, где одной из целей является поиск случайного дублирования знаний. Используйте shared-библиотеки (но осторожно!) для кода, который действительно является общим для нескольких проектов. И всегда задавайте вопрос: «Если это бизнес-правило изменится, сколько мест в коде нам придется обновить?». Ответ «одно» — верный признак того, что DRY защищен.

Принцип DRY — это не догма, а руководство к созданию поддерживаемого кода. Его сила не в устранении каждого повторяющегося символа, а в контроле над распространением знаний по системе. Умное следование этому принципу, с оглядкой на контекст и будущие изменения, отличает мастера от новичка.
386 3

Комментарии (13)

avatar
upa8x65o 28.03.2026
Всё верно. Истинный DRY уменьшает стоимость изменений. Если абстракция её увеличивает — это антипаттерн.
avatar
9s89o684t 28.03.2026
Спасибо за напоминание! Надо чаще задавать себе вопрос: 'Я устраняю знание или просто текст?'
avatar
2ti146koel3o 29.03.2026
Интересно, а как вы относитесь к принципу AHA (Avoid Hasty Abstractions) в противовес слепому DRY?
avatar
k72d3h2y3 29.03.2026
Ключевое — 'часть знания'. Если логика одинаковая — это DRY. Если просто код похож — не всегда.
avatar
lhl3su 30.03.2026
DRY — это про архитектуру, а не про код. Статья правильно расставляет акценты.
avatar
c7i9ltbai40o 30.03.2026
Хорошая мысль про хрупкие абстракции. Лучше повторение, чем зависимость от нестабильного модуля.
avatar
sum9o8o10n6 30.03.2026
Бывает, дублирование — это временно, на этапе исследования. Потом уже абстрагируешь с пониманием.
avatar
kqidk225j 30.03.2026
Отличная статья! Как раз недавно столкнулся с переусердствованием в DRY, которое привело к монолитной абстракции.
avatar
f38qwz 31.03.2026
Согласен, что контекст решает всё. Иногда дублирование кода — это меньшая цена, чем неправильная абстракция.
avatar
x70zljh 31.03.2026
Проблема в том, что 'единственное представление' иногда размазывается по 10 классам, и это хуже дублирования.
Вы просмотрели все комментарии