3 минуты
Оценка сложности кода
Упрощенчество — непонимание сложностей.
Простота — способность разрешать сложности.
Эрик-Эмманюэль Шмитт
Введение
Сегодня хотелось бы обсудить сложность кода и один из способов объективного подсчёта сложности. Мы не будем считать количество строк в метода, хотя этот параметр явно не стоит упускать из виду. Сегодня мы поговорим о Cognitive complexity
Проблема
Как вы понимаете, что один фрагмент кода является простым и более понятным, чем другой? Есть ли у вас чёткие критерии, по которым вы можете однозначно сказать какой из фрагментов кода проще?
При чтении кода можно увидеть, что метод может быть небольшого размера, но он сложнее, чем один из методов находящихся рядом с ним, даже если там куда больше строк кода или больше параметров. Обычно в такие моменты нет точных критериев, по которым можно сказать, чем этот код хуже другого.
В этой статье постараемся разобрать способ объективного определения сложности метода.
Решение
Компания Sonar разработала алгоритм, для подсчёта сложности кода, который давно используется в их продукте. Его реализацию также можно найти в моём анализаторе.
Идея в том, чтобы увеличивать метрику на единицу для оператора, который прерывает поток исполнения кода:
- Циклы [for, while, do …]
- Условные выражения, тернарные операторы и диррективы процессора [if, #if, #else, else …]
При увеличении вложенности (nested), мы также увеличиваем метрику.
Давайте рассмотрим пару примеров, для наглядности:
Пример с вложенностью
if (a) // +1
{
if (b) // +2 (N=1)
Console.WriteLine();
}
if (!c) // +1
Console.WriteLine();
Во время первого if, наш счётчик = 0, за конструкцию if мы добавляем 1 и опускаемся в тело if.
Внутри мы встречаем очередной if, за который как мы помним, должны дать ещё 1, но так как он является вложенным, то наш результат 1+1=2 и так со всеми выражениями.
Обратите внимание, что для последнего if, мы добавляем только 1, так как он не является вложенным.
Результатом работы этого метода будет complexity = 4. Когда у нас есть конкретное значение, с ним уже можно придти к разработчику, настроить статический анализатор на определенный порог срабатывания.
Пример с условными выражениями
// ↓ ↓ +2
var x = a || b && c && d;
Идея в том, чтобы добавлять 1 к нашей метрике после каждой смены оператора. В данном случае у нас сначала идёт ||, за это мы добавляем 1, после этого у нас идёт &&, мы проверяем, что предыдущий оператор отличался и добавляем ещё 1, в итоге получаем complexity = 2.
var x = a || b || c; // +1
В данном примере будет просто 1, так как у нас 1 оператор.
Для более подробной информацией об алгоритме, стоит обратится к paper от sonar.
Итог
Для оценки сложности кода можно использовать не только субъективную, но и объективную оценку, которая подкреплена алгоритмом подсчёта сложности кода.
Благодаря этому значению мы можем найти потенциальные проблемы со сложностью и принять решение провести рефакторинг.