Можете ли вы рассказать про зацепление и связность?

«Можете ли вы рассказать про зацепление и связность?» — вопрос из категории Архитектура, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Зацепление (Coupling) и Связность (Cohesion) — это фундаментальные метрики качества проектирования модулей (классов, компонентов).

1. Связность (Cohesion) — «насколько элементы внутри модуля связаны между собой».

  • Высокая связность — хорошо. Все методы и поля класса работают для достижения одной, четко определённой цели.
  • Пример класса с низкой связностью (плохо):
    class UserReport {
        public void generatePdf() { /* ... */ }
        public void calculateTax() { /* ... */ } // Не относится к отчётам!
        public void sendEmail() { /* ... */ } // Не относится к отчётам!
    }
  • Пример рефакторинга к высокой связности (хорошо):
    class UserReport { // Отвечает только за отчёты
        public void generatePdf() { /* ... */ }
        public void generateCsv() { /* ... */ }
    }
    class TaxCalculator { // Отвечает только за налоги
        public void calculateTax() { /* ... */ }
    }

2. Зацепление (Coupling) — «насколько один модуль зависит от деталей реализации другого».

  • Слабое (низкое) зацепление — хорошо. Изменения в одном классе минимально влияют на другие.
  • Пример сильного зацепления (плохо):
    class OrderProcessor {
        private MySQLDatabase db = new MySQLDatabase(); // Жёсткая привязка к конкретной реализации
        public void saveOrder(Order order) {
            db.query("INSERT INTO orders ..."); // Зависимость от деталей SQL
        }
    }
  • Пример слабого зацепления (хорошо) через Dependency Injection и интерфейсы:
    interface OrderRepository {
        void save(Order order);
    }
    class OrderProcessor {
        private OrderRepository repository; // Зависим от абстракции
        public OrderProcessor(OrderRepository repo) { // Внедрение зависимости
            this.repository = repo;
        }
        public void process(Order order) {
            repository.save(order); // Не знает деталей реализации (SQL, NoSQL, файл)
        }
    }

Идеал: Высокая связность и слабое зацепление. Это делает код более понятным, тестируемым, поддерживаемым и гибким к изменениям.