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

Что такое зацепленность?

1.8 Middle🔥 151 комментариев
#Python Core

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

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

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

Что такое зацепленность

Зацепленность (Coupling) — это степень зависимости между компонентами, модулями или классами программы. Высокая зацепленность означает, что изменение одного компонента требует изменения других. Низкая зацепленность — компоненты независимы друг от друга.

Высокая зацепленность (плохо)

# Классы сильно зависят друг от друга
class EmailSender:
    def send(self, message):
        print(f"Отправляю: {message}")

class UserService:
    def __init__(self):
        self.email_sender = EmailSender()  # Жёсткая зависимость
    
    def create_user(self, email, name):
        # Если изменить EmailSender, нужно менять UserService
        self.email_sender.send(f"Добро пожаловать, {name}!")
        # Создаём пользователя...

# Проблемы:
# 1. Сложно тестировать UserService без EmailSender
# 2. Нельзя использовать другую реализацию отправки
# 3. Изменение EmailSender влияет на UserService

Низкая зацепленность (хорошо)

# Использование зависимостей через параметры
class UserService:
    def __init__(self, email_sender):  # Инъекция зависимости
        self.email_sender = email_sender
    
    def create_user(self, email, name):
        self.email_sender.send(f"Добро пожаловать, {name}!")

# Использование
email_sender = EmailSender()
user_service = UserService(email_sender)

# Преимущества:
# 1. Легко подменять EmailSender на mock для тестов
# 2. Можно использовать любую реализацию sender-а
# 3. Изменения в одном классе не влияют на другой

Интерфейсы для снижения зацепленности

from abc import ABC, abstractmethod

# Определяем интерфейс (абстракция)
class MessageSender(ABC):
    @abstractmethod
    def send(self, message: str) -> None:
        pass

# Реализация для email
class EmailSender(MessageSender):
    def send(self, message: str) -> None:
        print(f"Отправляю email: {message}")

# Реализация для SMS
class SMSSender(MessageSender):
    def send(self, message: str) -> None:
        print(f"Отправляю SMS: {message}")

# UserService зависит от интерфейса, не от конкретной реализации
class UserService:
    def __init__(self, sender: MessageSender):
        self.sender = sender
    
    def create_user(self, identifier: str, name: str):
        self.sender.send(f"Добро пожаловать, {name}!")

# Можно использовать любой sender
email_sender = EmailSender()
sms_sender = SMSSender()

user_service_email = UserService(email_sender)
user_service_sms = UserService(sms_sender)

Виды зацепленности

1. Высокая связанность данных:

# Плохо — класс знает о внутренней структуре другого
class OrderProcessor:
    def process(self, user_dict):
        total = 0
        for order in user_dict['orders']:  # Зависит от структуры
            total += order['amount']

# Хорошо — работает через интерфейс
class User:
    def __init__(self, name, orders):
        self.name = name
        self._orders = orders
    
    def get_total_amount(self):
        return sum(order.amount for order in self._orders)

class OrderProcessor:
    def process(self, user):
        total = user.get_total_amount()  # Используем метод

2. Управление сложностью:

# Плохо — DatabaseService создаёт свой Database
class DatabaseService:
    def __init__(self):
        self.db = Database()  # Жёсткая зависимость

# Хорошо — Database передаётся извне
class DatabaseService:
    def __init__(self, db):
        self.db = db  # Инъекция

3. Циклические зависимости:

# Плохо — A зависит от B, B зависит от A
class UserService:
    def __init__(self, order_service):
        self.order_service = order_service

class OrderService:
    def __init__(self, user_service):
        self.user_service = user_service

# Это вызывает проблемы при инициализации и тестировании

Тестирование и зацепленность

Высокая зацепленность = сложно тестировать:

# Плохо
class PaymentProcessor:
    def process(self, amount):
        payment_gateway = RealPaymentGateway()  # Реальный API!
        return payment_gateway.charge(amount)

def test_payment():
    processor = PaymentProcessor()
    processor.process(100)  # Это запустит реальный платёж!

Низкая зацепленность = легко тестировать:

# Хорошо
class PaymentProcessor:
    def __init__(self, gateway):
        self.gateway = gateway
    
    def process(self, amount):
        return self.gateway.charge(amount)

def test_payment():
    mock_gateway = MockPaymentGateway()  # Подменяем
    processor = PaymentProcessor(mock_gateway)
    result = processor.process(100)
    assert result == "success"

Best Practices для снижения зацепленности

1. Используй инъекцию зависимостей:

# Плохо
class Service:
    def __init__(self):
        self.repo = Repository()

# Хорошо
class Service:
    def __init__(self, repo):
        self.repo = repo

2. Полагайся на интерфейсы, не на реализации:

from abc import ABC, abstractmethod

class UserRepository(ABC):
    @abstractmethod
    def find(self, user_id):
        pass

class PostgresUserRepository(UserRepository):
    def find(self, user_id):
        # SQL запрос
        pass

class UserService:
    def __init__(self, repo: UserRepository):
        self.repo = repo
    
    def get_user(self, user_id):
        return self.repo.find(user_id)

3. Избегай циклических зависимостей:

# Плохо — циклическая зависимость
# A -> B -> A

# Хорошо — одностороння зависимость
# A -> B, B -> C (никаких циклов)

4. Разделяй ответственность:

# Плохо — один класс отвечает за множество вещей
class User:
    def validate(self):
        pass
    
    def save_to_db(self):
        pass
    
    def send_email(self):
        pass

# Хорошо — разные классы для разных задач
class User:
    def validate(self):
        pass

class UserRepository:
    def save(self, user):
        pass

class UserNotifier:
    def send_welcome_email(self, user):
        pass

Измерение зацепленности

Признаки высокой зацепленности:

  • Сложно тестировать код без реальных зависимостей
  • Изменение одного класса требует изменения многих других
  • Много import-ов и жёстких зависимостей
  • Циклические зависимости между модулями

Признаки низкой зацепленности:

  • Код легко тестировать с mock-объектами
  • Изменение одного класса не влияет на другие
  • Минимум импортов, зависимости передаются через параметры
  • Нет циклических зависимостей

Низкая зацепленность — это ключ к чистому, тестируемому и масштабируемому коду.