Принцип 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 — это не догма, а руководство к созданию поддерживаемого кода. Его сила не в устранении каждого повторяющегося символа, а в контроле над распространением знаний по системе. Умное следование этому принципу, с оглядкой на контекст и будущие изменения, отличает мастера от новичка.
Защита принципа DRY: Секреты и советы от мастеров разработки
Статья раскрывает тонкости применения принципа DRY (Don't Repeat Yourself) на основе советов опытных разработчиков. Рассматривается различие между дублированием кода и знания, правило трех, преимущества композиции над наследованием, случаи стратегического дублирования, использование инструментов и опасность излишних абстракций. Цель — научиться применять DRY разумно для создания гибкого и поддерживаемого кода.
386
3
Комментарии (13)