← Назад к вопросам

Что такое DRY?

1.0 Junior🔥 193 комментариев
#Soft Skills и карьера

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

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Принцип DRY

DRY (Don't Repeat Yourself) — это фундаментальный принцип программирования, который декларирует: "Каждое знание должно иметь единственное, однозначное и авторитетное представление в системе". В более простой интерпретации это означает: "Не повторяй себя" — избегай дублирования логики, кода, конфигураций и любых других артефактов в кодовой базе.

Сущность принципа DRY

Принцип DRY направлен не только на механическое устранение повторяющихся строк кода, но и на борьбу с дублированием знаний — бизнес-правил, алгоритмов, условий, форматов данных, конфигурационных параметров. Дублирование этих элементов в разных частях программы создает серьезные риски:

  • Несогласованные изменения: при модификации логики в одном месте легко забыть обновить ее дубликат.
  • Усложнение поддержки: увеличение кодовой базы без добавления новой функциональности.
  • Рост вероятности ошибок: дублирование часто маскирует более глубокие проблемы архитектуры.

Пример нарушения DRY и его исправления в Go

Рассмотрим ситуацию, где знание (правило проверки почты и правила формирования приветствия) дублируется.

❌ Нарушение принципа DRY

package main

import "fmt"

func registerUserV1(email string, name string) {
    // Дублирование знания о валидации email
    if !strings.Contains(email, "@") {
        fmt.Println("Некорректный email")
        return
    }
    // Дублирование знания о формате приветствия
    fmt.Printf("Привет, %s! Ты зарегистрирован на %s\n", name, email)
}

func sendNewsletterV1(email string, name string) {
    // ТО ЖЕ САМОЕ правило валидации продублировано
    if !strings.Contains(email, "@") {
        fmt.Println("Невозможно отправить письмо: некорректный email")
        return
    }
    // ТО ЖЕ САМОЕ знание о формате приветствия продублировано
    fmt.Printf("Дорогой %s, для тебя новые акции! Пишем на %s\n", name, email)
}

Здесь знание о том, как проверять email и как обращаться к пользователю, разбросано по двум функциям. Изменение формата проверки (например, добавление проверки на точку) потребует правок в двух местах.

✅ Следование принципу DRY

package main

import (
    "fmt"
    "strings"
)

// Единственное авторитетное представление знания о валидации email
func isValidEmail(email string) bool {
    return strings.Contains(email, "@") && strings.Contains(email, ".")
}

// Единственное авторитетное представление знания о форматировании приветствия
func greetUser(name, email string) string {
    return fmt.Sprintf("Привет, %s! (контакт: %s)", name, email)
}

func registerUserV2(email string, name string) {
    if !isValidEmail(email) {
        fmt.Println("Некорректный email")
        return
    }
    fmt.Println(greetUser(name, email))
}

func sendNewsletterV2(email string, name string) {
    if !isValidEmail(email) {
        fmt.Println("Невозможно отправить письмо: некорректный email")
        return
    }
    message := fmt.Sprintf("Дорогой %s, для тебя новые акции! ", name)
    message += fmt.Sprintf("Напоминаем твой логин: %s", email)
    fmt.Println(message)
    // Используем общую функцию приветствия где это уместно
    fmt.Println("P.S.", greetUser(name, email))
}

Теперь правило валидации инкапсулировано в одной функции isValidEmail. При необходимости его изменения (добавление проверки домена, длины и т.д.) правка будет выполнена единожды в одном месте.

Способы применения DRY в Go

  1. Функции и методы: Самый базовый уровень — вынесение повторяющейся последовательности действий.
  2. Пакеты и модули: Создание внутренних пакетов (/internal/utils, /pkg/validation) для общих утилит, констант, типов.
  3. Интерфейсы (interface): Определение контрактов для поведения, которое может быть реализовано различными типами. Позволяет избежать дублирования кода, работающего с разными, но логически схожими сущностями.
  4. Встраивание (embedding): Композиция структур. Позволяет "наследовать" методы и поля, избегая дублирования их описания.
    type Base struct {
        ID uuid.UUID
        CreatedAt time.Time
    }
    // Знание об основных полях сущности не дублируется
    type User struct {
        Base        // Встраивание: User получает поля ID и CreatedAt
        Email string
    }
    type Order struct {
        Base        // Встраивание: Order получает поля ID и CreatedAt
        Amount int
    }
    
  5. Константы и переменные: Хранениеmagic numbers, строковых литералов, конфигурационных путей в одном месте.
  6. Генерация кода (go:generate): При необходимости создания большого количества похожего кода (например, методов для enum-типов) используется генерация из единственного источника истины (например, из .go файла с комментариями).

Важные предостережения

Слепая погоня за устранением повторения может привести к преждевременной абстракции и излишнему связыванию (coupling) частей системы, которые на самом деле меняются по разным причинам. Иногда дублирование лучше сохранить, если оно отражает разные знания, которые лишь случайно выглядят похоже на текущий момент. Следует бороться именно с дублированием знаний и намерений, а не просто с идентичными строками текста.

Таким образом, DRY в Go — это стратегия управления сложностью через создание единственного источника истины для каждого фрагмента логики, что напрямую ведет к повышению поддерживаемости, тестируемости и надежности кодовой базы.

Что такое DRY? | PrepBro