Перейти к содержанию

Правила связности

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

Смотрите также: LCOM (Недостаток связности методов) в правилах проектирования -- дополнительная метрика связности, считающая изолированные группы методов.


TCC -- Тесная связность класса

Metric ID: tcc

Что измеряет

TCC измеряет, насколько связаны публичные методы класса через общий доступ к свойствам. Если два публичных метода оба читают или пишут одно и то же свойство ($this->property), они считаются напрямую связанными.

TCC = NDC / NP

Где:

  • NDC = количество напрямую связанных пар методов (пары публичных методов, разделяющих хотя бы одно свойство)
  • NP = максимально возможное количество пар = N x (N - 1) / 2
  • N = количество отслеживаемых публичных методов

Результат -- отношение от 0.0 до 1.0:

  • TCC = 1.0 -- каждый публичный метод разделяет свойства с каждым другим. Класс идеально связный.
  • TCC >= 0.5 -- хорошая связность. Большинство методов работают с одними и теми же данными.
  • TCC < 0.3 -- низкая связность. Методы работают с разными подмножествами свойств -- класс, вероятно, имеет несколько ответственностей.

Представьте званый ужин: если каждый гость знает каждого другого гостя, группа тесно связана (TCC = 1.0). Если гости формируют изолированные клики без пересечений, лучше было бы провести два отдельных мероприятия (TCC близко к 0.0).

Как читать значение:

TCC Интерпретация
0.5--1.0 Хорошо -- методы хорошо связаны между собой
0.3--0.5 Умеренная связность
Ниже 0.3 Низкая связность -- рассмотрите разделение

Пороговые значения

TCC и LCC в настоящее время доступны как метрики (видимы в выводе --format=metrics). Они не генерируют нарушения самостоятельно. Используйте их вместе с LCOM для полной картины связности класса.

Рекомендуемая интерпретация:

Значение TCC Значение
1.0 Идеальная связность -- все публичные методы разделяют свойства
>= 0.5 Хорошая связность
0.3--0.5 Умеренная -- проверьте, не слишком ли много обязанностей у класса
< 0.3 Низкая связность -- класс, вероятно, нужно разделить

Пример

class OrderService
{
    private array $items = [];
    private float $total = 0.0;
    private string $customerEmail;
    private string $customerName;

    // Группа 1: работает с $items и $total
    public function addItem(string $item, float $price): void  // -> $this->items, $this->total
    {
        $this->items[] = $item;
        $this->total += $price;
    }

    public function getTotal(): float  // -> $this->total
    {
        return $this->total;
    }

    public function getItems(): array  // -> $this->items
    {
        return $this->items;
    }

    // Группа 2: работает с $customerEmail и $customerName
    public function setCustomer(string $name, string $email): void  // -> $this->customerName, $this->customerEmail
    {
        $this->customerName = $name;
        $this->customerEmail = $email;
    }

    public function getCustomerEmail(): string  // -> $this->customerEmail
    {
        return $this->customerEmail;
    }
}

С 5 публичными методами NP = 5 x 4 / 2 = 10 возможных пар. Только несколько пар разделяют свойства (например, addItem-getTotal разделяют $total, addItem-getItems разделяют $items, setCustomer-getCustomerEmail разделяют $customerEmail). Две группы не пересекаются, поэтому TCC будет низким (около 0.3).

Как исправить

  • Разделите класс по границам свойств. В примере: OrderCart для items/total и CustomerInfo для name/email.
  • Посмотрите, какие свойства группируются вместе. Методы, разделяющие свойства, принадлежат вместе; методы, которые не разделяют -- должны быть в отдельных классах.
  • Используйте значение TCC вместе с LCOM. LCOM считает изолированные группы; TCC показывает, какая доля пар методов связана. Вместе они дают полную картину связности.

LCC -- Свободная связность класса

Metric ID: lcc

Что измеряет

LCC расширяет TCC, включая транзитивные связи. Два метода считаются свободно связанными, если они соединены через цепочку напрямую связанных методов, даже если сами они не разделяют ни одного свойства.

LCC = NIC / NP

Где:

  • NIC = количество косвенно связанных пар (все пары, достижимые через прямые связи)
  • NP = максимально возможное количество пар

LCC всегда >= TCC для одного и того же класса. Если TCC = LCC, транзитивных связей нет. Если LCC значительно выше TCC, класс имеет «цепочечную» структуру, где методы связаны через посредников.

Соотношение Значение
TCC = LCC Все связи прямые -- нет транзитивных цепочек
LCC >> TCC Методы образуют цепочки -- проверьте, намеренна ли такая архитектура

Пример

Рассмотрим три метода A, B и C:

  • A и B оба обращаются к $this->data (напрямую связаны)
  • B и C оба обращаются к $this->cache (напрямую связаны)
  • A и C не разделяют свойств (не связаны напрямую)

TCC считает 2 прямые пары из 3 возможных: TCC = 2/3 = 0.67. LCC считает все 3 пары (A-C транзитивно связаны через B): LCC = 3/3 = 1.0.


Особенности реализации

Qualimetrix реализует упрощённый вариант спецификации TCC/LCC Bieman & Kang (1995):

  • Конструкторы и деструкторы (__construct, __destruct) исключены из набора методов по спецификации B&K (это методы настройки/очистки, а не поведенческие).
  • Перечисления (enums) исключены -- они не могут иметь свойств экземпляра, поэтому TCC всегда был бы 0.0, что вводит в заблуждение.
  • Интерфейсы исключены -- у них нет тел методов, поэтому доступ к свойствам невозможно измерить.
  • Учитываются только публичные методы. Оригинальная статья B&K указывает «видимые методы» (public + protected); Qualimetrix следует более строгому отраслевому соглашению (только public), что согласуется с большинством инструментов (PHPMD, PHPMetrics).
  • Учитывается только прямой доступ через $this->property. B&K также определяет «деревья вызовов», где публичный метод, вызывающий приватный вспомогательный метод, обращающийся к свойству, считается косвенным доступом. Это не реализовано -- делегирование через приватные методы не отслеживается. Это означает, что TCC может быть занижен для классов, активно использующих паттерн делегирования.
  • Статические и абстрактные методы исключены -- они не оперируют состоянием экземпляра.
  • Классы с 0 или 1 отслеживаемым публичным методом по умолчанию имеют TCC = 1.0 и LCC = 1.0 (класс с одним методом тривиально связный).

Сравнение с другими инструментами

Большинство инструментов (PHPMD, PHPMetrics, JArchitect) реализуют тот же упрощённый вариант -- только прямой доступ к свойствам, без деревьев вызовов. Значения должны быть сопоставимы между инструментами, хотя могут быть незначительные различия в обработке конструкторов или статических методов.


Настройка

TCC и LCC собираются как метрики и не имеют настраиваемых пороговых значений. Они отображаются в выводе metrics JSON:

bin/qmx check src/ --format=metrics

Для использования TCC/LCC в качестве контрольных показателей можно обрабатывать вывод metrics JSON программно (например, в скрипте CI-пайплайна).