Как измерить зацепление (coupling) в коде?

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

Ответ

Зацепление (coupling) — это мера взаимозависимости между модулями (классами, компонентами). Высокое зацепление усложняет поддержку и тестирование. Измеряется количественно и качественно.

1. Количественные метрики (можно автоматизировать):

  • Afferent Coupling (Ca): Количество внешних классов, которые зависят от данного класса. Высокое Ca означает, что класс имеет много "клиентов", и его изменение затронет многих.
  • Efferent Coupling (Ce): Количество внешних классов, от которых зависит данный класс. Высокое Ce означает, что класс хрупок, так как зависит от многих внешних изменений.
  • Instability (I = Ce / (Ca + Ce)): Показывает устойчивость класса к изменениям. I = 0 — абсолютно стабильный (много зависимостей от него, сам ни от кого не зависит). I = 1 — абсолютно нестабильный (полностью зависит от других).

2. Качественные признаки высокого зацепления (анализ кода):

  • Жесткие зависимости от конкретных классов: Использование new или прямых ссылок на конкретные реализации вместо абстракций (интерфейсов).
    // Плохо (высокое зацепление):
    class OrderProcessor {
        public function process() {
            $logger = new FileLogger('/var/log/app.log'); // Зависимость от конкретного класса
            $mailer = new SmtpMailer('smtp.gmail.com'); // И еще одна
        }
    }
    // Хорошо (низкое зацепление):
    class OrderProcessor {
        public function __construct(LoggerInterface $logger, MailerInterface $mailer) {
            // Зависит от абстракций
        }
    }
  • Нарушение Закона Деметры ("не разговаривай с незнакомцами"): Цепочки вызовов ($a->getB()->getC()->doSomething()) создают скрытые зависимости.
  • Использование глобальных состояний или синглтонов: Config::getInstance(), глобальные переменные.

3. Инструменты для статического анализа в PHP-экосистеме:

  • PHPMD (PHP Mess Detector): Правило CouplingBetweenObjects предупреждает о классах с чрезмерным количеством зависимостей.
  • PHPStan / Psalm: Могут детектировать зависимости и нарушения принципов SOLID (например, зависимость от конкретного класса вместо интерфейса).
  • dePHPend: Специализированный инструмент для визуализации графа зависимостей и расчета метрик (Ca, Ce, I).
  • Composer: Команда composer show --tree показывает дерево зависимостей пакетов.

Цель: Стремиться к низкому зацеплению и высокой связности (cohesion) внутри модулей. Зависимости должны быть направлены в сторону абстракций, а цепочки вызовов — минимальны.