Анализ цикломатической сложности кода



Инструмент для контроля сложности написанного кода для 1С8.
Пригодится разработчикам, которые заботяться о качестве своего продукта, простоте его тестирования и дальнейшей поддержке.

Если у вас есть опыт разработки информационных систем, вы наверняка знаете, что со временем (а иногда и сразу) написанный код усложняется и увеличивается в размере. Это приводит к тому, что становится сложно предсказать, к каким последствиям может привести модификация кода, а количество тестов, которые нужно провести, растет экспоненциально с ростом количества циклов и условий.

Иногда встречаются рекомендации по написанию вроде «старайтесь, что бы тексты ваших методов не превышали размера экрана». Очевидно, что это несерьезное требование, тем более, что в профессиональном мире давно уже существуют метрики на сложность кода. Одна из таких метрик — цикломатическая сложность. Что это и как она рассчитывается — можно почитать в википедии (статья на английском написана лучше).

Данный инструмент предназначен для расчета цикломатической сложности любого участка кода — фрагмент модуля, отдельного метода, целого модуля или набора модулей. Все менеджеры знают, что управлять чем-то возможно только тогда, когда это можно измерить. На мой взгляд, использование такой метрики сложности кода, как цикломатическая сложность, отлично подходит для выявления проблемых мест конфигурации, нахождения точек, которые требуют реинжиниринга. А данный инструмент поможет вам в этом.

Как использовать данный инструмент:

  1. Открыть обработку в 1С:Предприятии 8.
  2. Вставить фрагмент кода, который вы хотите проанализировать в поле на закладке «Текст»
    или выбрать каталог, в котором лежат искодные коды модулей (в текстовом виде), указав маску выбора фалов
    или выбрать файл с текстом исходного кода.
  3. Нажать на кнопку «Выполнить».
  4. Полученные результаты можно отсортировать по любой из колонок дерева результатов. Для наглядности, отдельные методы в дереве результатов подкрашиваются в цвет, символизирующий его сложность.
  5. На закладке «Настройки» можно поменять цвета, настройки градиента раскрашивания.

Как применять это в реальной жизни?

В комментариях попросили рассказать, как это применять в реальной жизни. Делюсь своими рекомендациями, основанными на  внедрении этого инструмента в нашем коллективе:

  • Оптимум метрики: не стОит вводить. Мы ограничились введением верхнего допустимого значения. Исследования, о которых говорится в википедии в разделе «Применение», рекомендуют ограничивать сложность на уровне 10. В отдельных случая можно допустить поднятие лимита до 15. Сложность более 50 гарантирует вам головную боль при поддержке.
    Учитывая специфику разработки ИС (мало математически строгих алгоритмов — всегда нужны какие-то исключения и допущения), мы подняли допустимую верхнюю границу сложности до 14. В отдельных случаях, допускается превышение до 20 с обязательным комментарием в коде метода, чем вызвана необходимость повышенной сложности.
  • Разработчик, при передаче задачи в тест, самостоятельно замеряет сложность кода который он произвел/модифицировал. При необходимости, некоторые части выносятся в отдельные методы. В ряде случаем это также увеличивает или облегчает повторное использование кода.
  • Для разработчиков, которые систематически превышают максимально допустимую сложность кода, можно применять штрафные санкции.
  • Кстати, не стоит брать типовые конфигурации в качестве ориентира по сложности кода. Самое высокое значение цикломатической сложности, которое я видел, было около 350. Yell И да, это был типовой регламентированный отчет.

Примечания по использованию:

  • Поддерживается анализ кода написанного как на русском, так и на английском языке.
  • Обработка работает в обычном (не управляемом) приложении, но исходный код может анализировать как для обычного, так и для управляемого приложения. Если вам нужна версия обработки для работы под управляемым приложением — пишите в комментариях или в личку.
  • Вы можете использовать обработку по своему усмотрению в рамках действующего законодательства вашего государства. Единственная просьба: если у вас есть замечания или предложения по улучшению обработки, а также в случае нахождения багов — пишите мне об этом в комментариях или мне в личку.

30 Comments

  1. hulio

    (0) Автор, объясните хотя бы в двух словах, чем полезна ваша обработка на практике? Какие выводы можно сделать, вычислив ЦСП?

    Reply
  2. comol

    И какое значение должно быть для нормального кода… может статистику по типовым собирали или ещё что. С чем сравнивать?

    Reply
  3. WKBAPKA

    я тоже не совсем понял как это работает, но за труды +

    Reply
  4. Spitfire

    (1) hulio, (2) comol, коллеги, по вашим просьбам добавил описание практического применения.

    вычисление ЦС дает ответ на вопрос, нужно ли уже разбить код на несколько методов, или еще нет.

    Reply
  5. comol

    (4) Spitfire, Вот теперь однозначно «+» и мой грейт респект.

    Reply
  6. DMSDeveloper

    Спасибо, нужная штука!

    Reply
  7. boggonzikov

    Спасибо, респект!

    Эх, еще бы такое в снегопат.

    Reply
  8. pumbaE

    (7) boggonzikov,

    Эх, еще бы такое в снегопат.

    Типа так можно хоть сейчас, а вот что бы все модули конфигурации перебрало и вывело сводный отчет — тут программировать надо.

    Reply
  9. Spitfire

    (8) pumbaE, перебрать все модули конфигурации можно через Конфигуратор-Выгрузить файлы конфигурации.

    Насчет сводного отчета — конкретизируйте, пожалуйста, как вы это видите. Возможно, я добавлю в саму обработку какие-то более удобные инструменты анализа результатов анализа =)

    Reply
  10. ildarovich

    На мой взгляд, нужно прямо в тексте статьи сказать, что данная метрика является просто суммой количества слов «Тогда» (then), «?(» и «Цикл» (loop) в тексте модуля (не находящихся в комментариях или кавычках). Иначе термин «цикломатическая сложность» заставляет представлять что-то чрезвычайно хитроумное. Ничего плохого в простоте метрики нет. Однако такое более понятное определение поставит ее в ряд с другими простыми метриками типа количества строк, букв, слов, разделителей «;» в одной процедуре (функции) модуля.

    Reply
  11. Spitfire

    (10) ildarovich, можно хоть весь листинг привести в описании. Зачем?

    И потом, обработка действительно считает цикломатическую сложость — количество циклов в графе управления программы. Если бы 1С была другой, например, многопоточной, такой простой реализации не получилось бы.

    И мне бы не хотелось, что бы эта метрика оказалась в одном ряду с подсчетом количества слов или строк метода. Почему? Потому что эти метрики на практике бесполезны, и интересуют только любителей больших чисел.

    Reply
  12. kser87

    Интересная штука.

    Reply
  13. pumbaE

    ОФФ: Странно, но в рассылке по почте стоит автором tormozit.

    (9) Spitfire, мое программировать — это в плане добавить в снегопате обход дерева метаданных и передачу текстов в вашу обработку, т.е. что бы не было необходимости нажимать выгрузить тексты модулей, потом запускать обработку и т.д., а нажал волшебную кнопку и сразу автоматом заработало.

    Имхо, для ежедневного использования непригодна, нет признаков игнорировать какой-то модуль; допустим типовая конфигурация, я все равно ничего не буду делать если вдруг ваша обработка покажет n, нет m число (m больше выглядит чем n). Т.е. сказать, а что изменилось за неделю получить такую информацию трудно.

    Тот же итоговый отчет необходимо строить только по граничным показателям, если у меня макс. стоит число 15, то зачем мне для одного из вариантов отчета видеть процедуры с 1 или 2?

    Reply
  14. tormozit

    (13) Я тут не причем =)

    Однако замечал во многих рассылках о комментариях с инфостарта, что я там как автор указан. Возможно кто первый подписался на комментарии, того рассылка и считает автором?

    Reply
  15. ildarovich

    (12) Если быть занудой, то

    количество циклов в графе управления программы

    обработка НЕ считает. Собственно, в приведенных Вами ссылках все это уже написано. В частности, и то, что основное назначение метрики — это оценка мощности множества тестов для процедуры или функции. При этом делается ПРЕДПОЛОЖЕНИЕ, что мощность теста определяется необходимостью прогона графа управления по всем возможным путям. На практике при программировании в 1С редко когда применяется (автоматическое) модульное тестирование или юнит-тестирование. Поэтому применение данной метрики — это определенная условность. И доказать, что эта метрика лучше, чем, например, число строк в модуле, невозможно из-за трудностей определения критерия сравнения.

    эти метрики на практике бесполезны, и интересуют только любителей больших чисел

    — думаю, что это высказывание просто голословно.

    Reply
  16. Spitfire

    (13) pumbaE, если честно, я не думал о потребности анализировать изменения за определенный период. мы у себя ее используем не так. Я подумаю о внедрении такой функциональности и о внедрении отборов в дерево результатов.

    Reply
  17. Spitfire

    (15) ildarovich, указанные вами предположения сделаны не мной, а множеством умных людей, занимающимися computer science. Аналогичные предположения делаются, когда производится любое автоматизированное тестирование — регрессионное, юнит, модульное, любое — ведь протестировать все случаи, с которыми придется столкнуться программе, невозомжно и ненужно. Потому каждый сам выбирает себе достаточное множество тестов. А эта метрика, как вы правильно сказали, помогает оценить мощность множества таких тестов. И, кстати, то, что среди разработчиков на 1С автоматическое тестирование применяется реже, чем в других технологиях, не делает нам чести. К счастью, ситуация улучшается, я могу это видеть.

    Если быть занудой, то

    количество циклов в графе управления программы

    обработка НЕ считает.

    Это равносильно утверждению, что половина произведения катетов прямоугольного треугольника не дает нам его площади. А дает лишь.. хм.. половину произведения катетов.

    К сожалению, эта метрика придумана не мной, потому я не буду здесь ее защищать. Ее полезность изучена и доказана.

    эти метрики на практике бесполезны, и интересуют только любителей больших чисел

    — думаю, что это высказывание просто голословно.

    в таком случае, приведите, пожалуйста, практическую ценность для разработчика числа строк или символов модуля. И объясните, что буду означать изменения этих метрик при добавлении/удалении больших комментариев или запросов.

    Reply
  18. Qsko

    Очень интересная разработка. Очень порадовала фраза:

    Кстати, не стоит брать типовые конфигурации в качестве ориентира по сложности кода. Самое высокое значение цикломатической сложности, которое я видел, было около 350. И да, это был типовой регламентированный отчет.

    О чём говорят эти замеры? На сколько я понял, главное, о чём говорит нам числовой показатель — «количество» учитываемых условий/условностей в каком-либо методе. Причём, если следовать рекомендациям, то эти учитываемые условия следует минимизировать, вынося их просчёт в отдельные процедуры и функции.

    Забавно, что в типовых конфигурациях самыми «сложными» оказались те, что отвечают за:

    1) вывод печатных (ПечатьСчетаФактуры1137 — 39) и регламентированной отчётности (ПроверитьВозможностьВыгрузки — 131),

    2) процедуры проведения документов по регистрам.

    Хотя мне кажется, что если на одном уровне встречается «Если… Тогда… КонецЕсли», а за ним следует снова «Если… Тогда… КонецЕсли», то это не усложнят код (условия не вложены, на одном уровне), а на числовой показатель влияют: +1 за каждое Если… Тогда… КонецЕсли. Ибо эти проверялки условий — необходимость, которую выносить в отдельные функции вряд ли стоит. Посмотрите функции «ПроверитьВозможностьВыгрузки» в регламентированных отчётах.

    По сему такая мысль: а может не надо при следующими друг за другом «Если… Тогда… КонецЕсли» на одном уровне увеличивать счётчик на +1? А вот за вложенность можно увеличивать. Чем больше вложенностей условий, тем запутаннее код. Тоже самое касается и циклов. Но у них другая беда — много циклов гораздо хуже, чем тоже количество «Если…».

    Однако, метод есть метод. Он лишь ориентирует на ВОЗМОЖНО проблемные места, а не является показателем проблемности. То есть условие необходимое, но не достаточное.

    Reply
  19. pumbaE

    (18) Qsko, ну еще в зарплатах модулях очень любят использовать, для возможности группировки.

    Если Истина Тогда 

    (возможно потому, что у разрабов типовых снегопата нет 🙂 )

    Reply
  20. LaNaite

    Интересно! Респект!

    Reply
  21. ildarovich
    Reply
  22. ildarovich

    (21) Могу также добавить, что существует по крайней мере четыре приема, позволяющие намеренно занижать цикломатическую сложность, считаемую Вашей обработкой:

    1) «Раскрытие циклов» — повторение одинаковых строк в программе, если число повторений заранее известно;

    2) Использование оператора «Выполнить» — кавычки будут экранировать Тогда и Цикл от подсчета;

    3) Использование списка (или таблицы) значений и поиска в нем вместо Если … ИначеЕсли … ИначеЕсли … КонецЕсли;

    4) Использование рекурсии вместо циклов. При этом вместо условия выхода использовать неполноту вычисления логических выражений (не вполне уверен, но стоит попробовать).

    (11) Если привести в статье более понятное определение, то разработчики, желающие разрабатывать код со значением сложности менее 10, будут просто складывать число циклов и условных операторов (считать до 10 не сложно) вместо более «цикломатически-сложной» (шутка) процедуры запуска обработки.

    Тем не менее статью считаю полезной, поставил плюс, метрику возьму на вооружение. Ну и ради спортивного интереса код в обработке нужно попытаться привести к меньшим значениям цикломатической сложности!

    P.S.: Думаю, что еще одна область применения алгоритма оценки — это написание на его основе нового «правила проверки» для конфигурации «1С:Автоматизированная проверка конфигураций». Плюс в том, что там уже есть готовые функции «ПолучитьТекстМодуля(ИмяМодуля)», «ПолучитьСтруктуруМодуля(ИмяМодуля)»

    Reply
  23. ManyakRus

    отчет работает 🙂

    показывает что надо, правильно.

    Только оно не поможет заставить программистов исправить свой код,

    скажут что у меня сложные вычисления поэтому и сложный код..

    (у меня доходит до 37 ЦС, у коллеги до 75 ЦС)

    Reply
  24. Spitfire

    (23) ManyakRus, как показывает практика, задачи класса «заставить людей что-то делать» или «заставить людей что-то делать так, как нужно» решаются другими способами. 🙂 Это отдельная область знаний.

    Reply
  25. ildarovich

    Понадобилось воспользоваться обработкой… Нашел ошибку: считаются лексемы «Тогда», но не считаются «тогда» и тому подобное. Понятно, при подсчете нужно приводить строки к одному регистру. Кстати, непонятно почему не используется встроенная функция СтрЧислоВхождений(<Строка>, <ПодстрокаПоиска>), а написана своя.

    Reply
  26. Spitfire

    (25) ildarovich, Спасибо за замечание, выложил исправленную версию.

    Свой метод подсчета вхождений пришлось писать потому, что СтрЧислоВхождений не умеет искать в режиме «слово целиком», то есть, он посчитает вхождения инструкций в идентификаторы.

    Reply
  27. bashhhh

    Протестируем все свои наработки. Спасибо за отчет!

    Reply
  28. vipetrov2

    Надо эту характеристику рассматривать не как главный критерий написания кода, который нужно минимизировать, а как одно из ограничений. А то подобный догматизм может привести к таким бесполезным вещам, как «Индусский код».

    И получается, что оператор ветвления в цикле нужно считать не за единицу, а умножать на количество циклов. Интересно посмотреть, что за граф построите в алгоритме сортировки.

    И оператором ветвления является не только «Если», но и «Попытка».

    Reply
  29. ImHunter

    Похоже, разработчик уже не поддерживает обработку, т.к. был на сайте в 2015 году.

    Кто будет пользовать — в обработке нужно фикс сделать:

    Процедура УбратьДирективыКомпиляции(Текст, Директива) Экспорт
    ПозицияНачалаДирективы = НайтиСледующееВхождениеПодстроки(Текст, Директива);
    Пока ПозицияНачалаДирективы <> 0 Цикл
    НачалоТекущейСтроки = НайтиПредыдущееВхождениеПодстроки(Текст, Символы.ПС, ПозицияНачалаДирективы);
    Если НачалоТекущейСтроки = 0 Тогда
    НачалоТекущейСтроки = 1;
    КонецЕсли;
    
    СтрокаПередХешем = Сред(Текст, НачалоТекущейСтроки, ПозицияНачалаДирективы — НачалоТекущейСтроки);
    
    // директивы не могут стоять после других команд или текста в строке — перед ними могут быть только незначащие символы
    Если СокрЛП(СтрокаПередХешем) = «» Тогда
    НачалоСледующейСтроки = НайтиСледующееВхождениеПодстроки(Текст, Символы.ПС, ПозицияНачалаДирективы + 1);
    Если НачалоСледующейСтроки = 0 Тогда
    НачалоСледующейСтроки = СтрДлина(Текст);
    КонецЕсли;
    Текст = ?(НачалоТекущейСтроки > 1, Лев(Текст, НачалоТекущейСтроки), «») + Сред(Текст, НачалоСледующейСтроки);
    КонецЕсли;
    
    //  фикс от
    НоваяПозиция = НайтиСледующееВхождениеПодстроки(Текст, Директива, ПозицияНачалаДирективы);
    Если НоваяПозиция=ПозицияНачалаДирективы Тогда
    ПозицияНачалаДирективы = ПозицияНачалаДирективы + 1;
    КонецЕсли;
    // фикс до
    ПозицияНачалаДирективы = НайтиСледующееВхождениеПодстроки(Текст, Директива, ПозицияНачалаДирективы);
    КонецЦикла;
    КонецПроцедуры
    

    Показать

    Reply
  30. ImHunter

    Не, вставленный сходу фикс искажает алгоритмы и формирует неверный результат. Надо все-таки еще подумать.

    А смысл фикса задумывался в том, чтобы выйти из бесконечного цикла.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *