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

Связано ли DRY со связанностью

2.3 Middle🔥 141 комментариев
#Python Core#Soft Skills#Архитектура и паттерны

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

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

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

DRY и связанность кода

Да, принцип DRY (Don't Repeat Yourself) тесно связан с концепцией связанности или сцепления кода. Однако это отношение неоднозначное и требует аккуратного понимания.

Что такое DRY?

DRY — принцип разработки, который гласит: каждая часть функциональности должна быть реализована ровно один раз в коде. Идея состоит в том, что не должно быть дублирования логики.

Что такое связанность?

Связанность (coupling) — это степень, в которой один модуль зависит от другого. Высокая связанность означает, что изменение одного компонента требует изменения других.

Связь между ними

Перво-наперво: DRY НЕ всегда является причиной высокой связанности, но неправильное применение DRY может её увеличить.

Пример 1: DRY как причина связанности

# ❌ Плохо: извлекли повторяющийся код, но создали нежелательную зависимость

class UserService:
    def get_user_email(self, user_id):
        # Получение email из базы
        return database.query(f"SELECT email FROM users WHERE id = {user_id}")

class OrderService:
    def send_order_confirmation(self, order_id):
        # Используем тот же запрос для получения email
        email = UserService().get_user_email(self.get_user_id_for_order(order_id))
        self.send_email(email)

class NotificationService:
    def notify_user(self, user_id):
        # И снова повторяем логику
        email = UserService().get_user_email(user_id)
        self.send_notification(email)

Видим повторение get_user_email(), извлекли в отдельный метод — кажется, DRY выполнили. Но теперь OrderService и NotificationService сильно связаны с UserService. Если изменить сигнатуру метода или его поведение, нужно обновлять обе служ.

Пример 2: DRY без излишней связанности

# ✅ Хорошо: извлекли логику, но разделили ответственность

class UserRepository:
    """Отвечает за получение данных пользователя"""
    def get_email(self, user_id: int) -> str:
        return database.query(f"SELECT email FROM users WHERE id = {user_id}")

class UserService:
    """Бизнес-логика работы с пользователями"""
    def __init__(self, repository: UserRepository):
        self.repository = repository
    
    def get_user_email(self, user_id: int) -> str:
        return self.repository.get_email(user_id)

class OrderService:
    """Работает только с абстракцией"""
    def __init__(self, user_service: UserService):
        self.user_service = user_service
    
    def send_order_confirmation(self, order_id: int):
        user_id = self.get_user_id_for_order(order_id)
        email = self.user_service.get_user_email(user_id)
        self.send_email(email)

class NotificationService:
    """Также использует абстракцию"""
    def __init__(self, user_service: UserService):
        self.user_service = user_service
    
    def notify_user(self, user_id: int):
        email = self.user_service.get_user_email(user_id)
        self.send_notification(email)

Здесь мы:

  • Исключили дублирование (DRY)
  • Использовали Dependency Injection для слабой связанности (Low Coupling)
  • Разделили ответственность (Single Responsibility)

Проблема с "неправильным" DRY

Основная ошибка: извлекать в отдельную функцию код, который совпадает синтаксически, но семантически различен.

# ❌ Плохо: синтаксическое сходство, но разные смыслы

def format_value(value):
    """Общая функция для всех форматирований"""
    return f"{value:.2f}"

class PriceFormatter:
    def format(self, price):
        return format_value(price)  # Используем общую функцию

class PercentageFormatter:
    def format(self, percentage):
        return format_value(percentage)  # Используем ту же функцию

Проблема: если требования поменяются, цены должны округляться до 2 знаков, а проценты до 1, мы создадим излишнюю связанность.

# ✅ Хорошо: разделили по смыслу

class PriceFormatter:
    @staticmethod
    def format(price: float) -> str:
        return f"${price:.2f}"

class PercentageFormatter:
    @staticmethod
    def format(percentage: float) -> str:
        return f"{percentage:.1f}%"

Правильный баланс

  1. Исключай дублирование — когда логика действительно одна и та же и по смыслу, и по реализации
  2. Избегай излишней связанности — используй абстракции (interfaces, dependency injection)
  3. Разделяй по ответственности — один класс/модуль = одна задача
  4. Прежде всего подумай — прежде чем извлекать код, убедись, что он действительно должен быть общим

Вывод: DRY и слабая связанность идут рука об руку, если правильно применять оба принципа. DRY помогает уменьшить объём кода, а правильная архитектура помогает избежать излишней связанности при этом.