Розуміння цикломатичної складності


11

Нещодавно я зіткнувся з цикломатичною складністю і хотів би спробувати зрозуміти це краще.

Наведіть декілька практичних прикладів кодування різних факторів, які входять до обчислення складності? Зокрема, для рівняння Вікіпедії M = E − N + 2Pя хочу краще зрозуміти, що означає кожен з наступних термінів:

  • E = кількість ребер графіка
  • N = кількість вузлів графіка
  • P = кількість підключених компонентів

Я підозрюю, що або E, або N може бути числом пунктів прийняття рішення (якщо, інакше, якщо, для, передбачити тощо) в блоці коду, але я не зовсім впевнений, що саме означає або що означає інше. Я також здогадуюсь, що P посилається на функціональні виклики та інстанції класів, але не існує чіткого визначення, враховуючи те, що я бачу. Якщо хтось міг би пролити трохи більше світла на кілька чітких прикладів коду кожного, це допоможе.

Як подальший результат, цикломатична складність безпосередньо співвідноситься з кількістю одиничних тестів, необхідних для 100% покриття шляху ? Як приклад, чи вказує метод зі складністю 4, що для покриття цього методу потрібно 4 одиничні тести?

Нарешті, чи впливають регулярні вирази на цикломатичну складність, і якщо так, то як?


Я виявив, що ви можете отримати оригінальний папір МакКейба з Вікіпедії, а Google Books видасть книгу, яку МакКейб використав для свого оригінального документа. Цікаво, що потім ви побачите, що МакКейб неправильно використав оригінальну теорему (а також заплутано пояснює, що він повинен починатись з непрямої графіки, і не потрібно в першу чергу сильно з’єднуватися), але цифри все одно виходять правильно ( правильна формула була б M = E + 1-N + P, але оскільки P завжди дорівнює 1, вона відповідає ...) Думка виникає, що сучасне "Обробка винятків" кидає ключ до творів цієї метрики.
Девід Тонхофер

... а що стосується рекурсивних дзвінків (можливо, через ланцюг функцій). Чи можна сплавити графіки функцій? Як щодо короткого замикання булевих операторів типу "&&". Оператори, що охороняються, такі як "ref? .X", які приносять нуль, якщо ref - нуль? Ну добре, це просто ще одна метрика. Але тут є якась робота для маленького університетського проекту.
Девід Тонхофер

Відповіді:


8

Щодо формули: вузли представляють стани, ребра представляють зміни стану. У кожній програмі висловлювання вносять зміни в стан програми. Кожне послідовне твердження представлене ребром, а стан програми після (або перед ...) виконанням оператора є вузлом.

Якщо у вас є твердження про розгалуження ( ifнаприклад) - тоді у вас виходять два вузли, оскільки стан може змінюватися двома способами.

Інший спосіб обчислити число цикломатичної складності (CCN) - це обчислити кількість "регіонів" у виконаному вами графіку (де "незалежна область" - це коло, яке не містить інших кіл). У цьому випадку CCN буде кількістю незалежних регіонів плюс 1 (що було б точно таким же числом, як дає попередня формула).

CCN використовується для покриття розгалуження або покриття контуру , яке є однаковим. CCN дорівнює кількості різних шляхів розгалуження, теоретично можливих в одному потоковому додатку (який може включати гілки типу " if x < 2 and x > 5 then", але це повинен бути сприйнятий хорошим компілятором як недоступний код). Ви повинні мати принаймні таку кількість різних тестових випадків (може бути більше, оскільки деякі тестові випадки можуть повторювати шляхи, охоплені попередніми, але не менше, якщо кожен випадок охоплює один шлях). Якщо ви не можете покрити шлях яким-небудь можливим тестовим випадком - ви знайшли недоступний код (хоча вам потрібно буде фактично довести собі, чому він недоступний, ймовірно, x < 2 and x > 5десь ховається десь).

Щодо регулярних виразів - вони, звичайно, впливають, як і будь-який інший фрагмент коду. Однак, CCN конструкції регулярних виразів, ймовірно, занадто високий, щоб охопити один тест одиниці, і ви можете припустити, що двигун регулярного випробовування був протестований, і ігнорувати потенціал розгалуження виразів для ваших потреб тестування (якщо ви не тестуєте свій regex двигун, звичайно).


2
+1: Насправді, ви повинні довіряти, що двигун регулярного випробування був протестований. Якщо ви не довіряєте, отримати той , який ви робите довіру.
С.Лотт

"CCN дорівнює кількості можливих різних шляхів виконання в одному потоковому додатку" Це неправильно, оскільки CCN заснований саме на топології коду, а не на його значенні . Хороший відсоток цих шляхів може бути неможливим здійснити, оскільки вони вимагають вхідного стану, який неможливо встановити (деякі х 5, а також менше 2, наприклад). Чесно кажучи, я вважаю, що використання CCN для вирішення тестових випадків для запуску є химерним. CCN - це номер, який повідомляє розробнику "ви, можливо, тут зайшли за борт, будь ласка, подумайте про рефакторинг". І навіть тоді можуть бути вагомі причини для високої CCN.
Девід Тонхофер

1
@David додав речення для вирішення цього питання. CCN - це галузеве покриття, і ніколи не буває вагомих причин високої CCN на нижчому рівні (як правило, я пропоную виконувати за кожною функцією).
littleadv

Покриття гілки та покриття контуру неоднакові. Покриття галузей має на меті охопити всі гілки, тоді як покриття контуру спрямоване на охоплення всіх комбінацій гілок.
mouviciel

13

Деякі зауваження з цього приводу, що я складно пишу ...

Зокрема, для Вікіпедії рівняння M = E - N + 2P

Це рівняння дуже неправильне .

Чомусь МакКейб справді використовує це у своєму первісному документі ("Захід про складність", IEEE Transaction on Software Engineering, Vo .. SE-2, No.4, грудень 1976 р.), Але не виправдовуючи це і після фактичного посилання на правильне формула на першій сторінці, яка є

v (G) = e - v + p

(Тут елементи формули були відновлені)

Зокрема, МакКейб посилається на книгу C.Berge, Graphs and Hypergraphs (скорочено нижче G&HG). Безпосередньо з цієї книги :

Визначення (стор. 27 внизу G&HG):

Цикломатичне число v (G) (непрямого) графіка G (який може мати декілька від'єднаних компонентів) визначається як:

v (G) = e - v + p

де e = кількість ребер, v = кількість вершин, p = кількість з'єднаних компонентів

Теорема (стор. 29 вгорі G&HG) (не використовується McCabe):

Цикломатичне число v (G) графа G дорівнює максимальній кількості незалежних циклів

Цикл являє собою послідовність вершин , починаючи і закінчуючи в одній вершині, з кожними двома послідовними вершинами в послідовності суміжних один з одним в графіку.

Інтуїтивно зрозумілий набір циклів не залежить, якщо жоден з циклів не може бути побудований з інших шляхом накладення прогулянок.

Теорема (стор. 29, середина G&HG) (як використовує МакКейб):

У сильно зв'язаному графіку G цикломатичне число дорівнює максимальній кількості лінійно незалежних ланцюгів.

Схема є циклом, без повторень вершин і ребер , дозволених.

Кажуть, що спрямований графік сильно пов'язаний, якщо кожна вершина досягається від кожної іншої вершини, проходячи через ребра в їх визначеному напрямку.

Зауважте, що тут ми перейшли від ненаправлених графіків до сильно з’єднаних графіків (які спрямовані ... Берж не робить це повністю зрозумілим)

МакКейб зараз застосовує вищевказану теорему, щоб отримати простий спосіб обчислити таким чином "число цикломатичної складності МакКабі" (CCN):

Даний спрямований графік, що представляє "топологію стрибка" процедури (графік потоку інструкцій), з позначеною вершиною, що представляє унікальну точку входу, і позначеною вершиною, що представляє унікальну точку виходу (вершину точки виходу може знадобитися "побудувати" додавши його у випадку декількох повернень), створіть сильно з’єднаний графік, додавши спрямований край від вершини точки виходу до вершини точки вступу, зробивши таким чином вершину точки входу доступною для будь-якої іншої вершини.

МакКейб зараз вважає (досить заплутано я можу сказати), що цикломатичне число модифікованого графіка потоку інструкцій "відповідає нашому інтуїтивному поняттю" мінімальна кількість шляхів ", і тому ми будемо використовувати це число як міру складності.

Класно, так:

Цикломатичний номер складності модифікованого графіка потоку інструкцій може бути визначений шляхом підрахунку "найменших" схем у непрямому графіку. Це не особливо важко зробити людиною чи машиною, але застосування вищевказаної теореми дає нам ще простіший спосіб її визначення:

v (G) = e - v + p

якщо нехтувати спрямованістю ребер.

У всіх випадках ми просто розглядаємо одну процедуру, тому в усьому графіку є лише один підключений компонент, і так:

v (G) = e - v + 1.

Якщо ви вважаєте оригінальний графік без доданого краю "вихід-вхід" , ви отримуєте просто:

ṽ (G) = ẽ - v + 2

як ẽ = e - 1

Давайте проілюструємо, використовуючи приклад МакКейба з його статті:

Приклад МакКейба

Тут ми маємо:

  • e = 10
  • v = 6
  • p = 1 (одна складова)
  • v (G) = 5 (ми чітко підраховуємо 5 циклів)

Формула цикломатичного числа говорить:

v (G) = e - v + p

який дає 5 = 10 - 6 + 1 і так правильно!

"Цикломатичний номер складності МакКейба", як зазначено в його статті, є

5 = 9 - 6 + 2 (більше жодних пояснень у роботі не наводиться)

що буває правильним (він дає v (G)), але з неправильних причин, тобто ми використовуємо:

ṽ (G) = ẽ - v + 2

і, отже, ṽ (G) = v (G) ... феу!

Але чи корисний цей захід?

Два слова: Не дуже

  • Не зовсім зрозуміло, як встановити "графік потоку інструкцій" процедури, особливо, якщо обробка виключень та рекурсія входять у зображення. Зауважимо, що МакКейб застосував свою ідею до коду, написаного на FORTRAN 66 , мові без рекурсії, без винятків та прямої структури виконання.
  • Те, що процедура з рішенням і процедура з циклом дають ту саму CCN, не є хорошим знаком.

введіть тут опис зображення


1
@JayElston Хороший улов. Дійсно, я. Виправлено!
Девід Тонхофер

1
Великий +1 для посилання на оригінальний папір. Багато робіт, написаних приблизно в той час, є досить читабельними для будь-якого програміста середнього рівня, і їх слід прочитати.
Даніель Т.

1

Як подальший результат, цикломатична складність безпосередньо співвідноситься з кількістю одиничних тестів, необхідних для 100% покриття шляху?

Так, в основному. Також непогано використовувати цикломатичну складність як показник, коли потрібно робити рефактор. З мого досвіду, тестабельність та повторне використання значно збільшуються для нижчого рівня CC (хоча ви повинні бути практичними - не перенапружуйте, а деякі методи матимуть високий рівень CC через свій характер - це не завжди має сенс спробувати нижній).

Нарешті, чи впливають регулярні вирази на цикломатичну складність, і якщо так, то як?

Так, якщо ви хочете бути точними, хоча більшість інструментів аналізу коду, здається, не враховують їх таким чином. Регулярні вирази - це лише машини кінцевого стану, тому я здогадуюсь, що їх CC можна було б обчислити з графіка FSM, але це було б досить велика кількість.


+1 - Я здогадуюсь, що обчислити CC для RegExes - не цікаве завдання.
VirtuosiMedia
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.