Cyclomatic сложность при вызове одного и того же метода несколько раз

12

Благодаря вопросу в Code Review у меня возникло небольшое разногласие (которое, по сути, является возможностью чему-то научиться) о том, что именно является Cyclomatic Complexity для приведенного ниже кода.

public static void main(String[] args) {
    try {
        thro();
        thro();
        thro();
        thro();
        thro();
        thro();
        thro();
    }
    catch (NullPointerException e) {
    }
}

private static Random random = new Random();

public static void thro() throws NullPointerException {
    if (random.nextBoolean())
        throw new NullPointerException();
    System.out.println("No crash this time");
}

При написании этого кода в Eclipse и использовании плагина метрик Eclipse он говорит мне, что цикломатическая сложность McCabe для основного метода равна 2, а для throметода - 2.

Однако кто-то еще говорит мне, что сложность вызова throнесколько раз равна number of calls * method complexity, и поэтому утверждает, что сложность основного метода равна 7 * 2 = 14.

Мы измеряем разные вещи? Можем ли мы оба быть правильными? Или какова здесь настоящая цикломатическая сложность?

Саймон Форсберг
источник
5
CC функции - это два, так как есть только два пути. СС программы выше. Это полный удар в темноте, но я предполагаю, что программное обеспечение для анализа кода воспринимает каждую функцию как отдельный черный ящик из-за невозможности вычисления CC всего сложного приложения за один раз.
Фоши
@Phoshi Если вы напишете это в качестве ответа и (если возможно) предоставите ссылки, показывающие, что существует разделение, я с радостью приму этот ответ.
Саймон Форсберг
Если вы посчитаете все пути, вызванные возможными исключениями в измерениях CC,
дай

Ответы:

9

Когда я правильно понял это, Cyclomatic Complexity of mainis 8 - это число линейно независимых путей в коде. Вы либо получаете исключение в одной из семи строк, либо ни в одной, но не более чем в одну. Каждая из этих возможных «точек исключения» в точности соответствует одному другому пути в коде.

Я предполагаю, что когда МакКейб изобрел этот показатель, у него не было языков программирования с учетом обработки исключений.

Док Браун
источник
Но действительно ли имеет значение, какая из строк выдает исключение?
Саймон Форсберг
5
@ SimonAndréForsberg: да, это так. Представьте, что у «thro» есть побочный эффект, когда он увеличивает глобальный счетчик при его вызове (это не изменит возможные пути через код). Возможные результаты этого счетчика в этом случае от 0 до 7, так что это доказывает, что CC равен по крайней мере 8.
Док Браун,
Вы сказали бы, что плагин метрик, который я использую, сообщает о неверном значении для mainметода?
Саймон Форсберг
@ SimonAndréForsberg: ну, я не знаю твой плагин метрик, но 2, очевидно, не 8.
Док Браун
В моем вопросе есть ссылка на плагин метрик ....
Саймон Форсберг,
6

Будучи «другим парнем», я отвечу здесь и буду точен в отношении того, что я говорю (что я не особенно точно описал в других формулах).

Используя приведенный выше пример кода, я вычисляю цикломатическую сложность как 8, и у меня есть комментарии в коде, чтобы показать, как я это вычисляю. Для описания пути я буду считать успешную проходной все те thro()вызовы , как «основным» «кодом путь» (или «CP = 1»):

public static void main(String[] args) {
  try {
             // This is the 'main' Code Path: CP = 1
    thro();  // this has a branch, can succeed CP=1 or throw CP=2
    thro();  // this has a branch, can succeed CP=1 or throw CP=3
    thro();  // this has a branch, can succeed CP=1 or throw CP=4
    thro();  // this has a branch, can succeed CP=1 or throw CP=5
    thro();  // this has a branch, can succeed CP=1 or throw CP=6
    thro();  // this has a branch, can succeed CP=1 or throw CP=7
    thro();  // this has a branch, can succeed CP=1 or throw CP=8
  }
  catch (NullPointerException e) {
  }
}

Итак, я считаю 8 путей кода в этом основном методе, который для меня является цикломатической сложностью 8.

С точки зрения Java, каждый механизм выхода из функции имеет значение для его сложности, поэтому метод, имеющий состояние успеха и, например, выбрасывающий, возможно, до 3 исключений, имеет 4 документированных пути выхода.

Сложность метода, вызывающего такую ​​функцию, такова:

CC(method) = 1 + sum (methodCallComplexity - 1)

Я думаю, что нужно учитывать и то, что, по моему мнению, catchпредложение не вносит вклад в сложность метода, catchявляется просто целью throwsветви, и, таким образом, блок перехвата, который является целью нескольких throwподсчетов s 1 раз для каждого throw, а не только один раз для всего.

rolfl
источник
Вы тоже подсчитываете возможные ветви для исключений OutOfMemoryException? Я имею в виду педантично, что они могут вызвать ответвления кода, но никто не считает их, поскольку они ослабляют полезность метрики.
Теластин
Нет, я не ... и вы правы, но в контексте этого аргумента я считаю только те исключения, которые объявил метод. Кроме того, если метод объявляет три исключения, но код callinch делает, catch (Throwable t) {...то я догадываюсь, что не имеет значения, сколько исключений он объявляет.
rolfl