Почему цикломатическая сложность этой функции равна 12?

У меня есть функция (С#), которая проверяет четыре набора условий и возвращает логическое значение. Если какое-либо из них верно, возвращается true. Я уверен, что мог бы упростить логику, но я хочу, чтобы она была достаточно читаемой.

Расширение CodeMaid в Visual Studios сообщает мне, что цикломатическая сложность функции равна 12. Я просмотрел и силоматическая сложность

количество независимых путей через исходный код

Я не понимаю, почему это 12. Я могу думать об этом двояко: либо цикломатическая сложность должна быть равна 2, потому что она всегда проходит по одному и тому же пути, но может вернуть либо true, либо false. Или мог бы понять, если бы это было 16, потому что четыре логических значения or, сложенные вместе в конце, могли быть истинными или ложными, 2*2*2*2 = 16.

Может кто подскажет почему 12? Может быть, даже показать схему, чтобы я мог визуализировать разные пути?

public bool FitsCheckBoxCriteria(TaskClass tasks)
{
    // note: bool == true/false comparisons mean you don't have to cast 'bool?' as bool


    // if neither checkboxes are checked, show everything
    bool showEverything = NoShutDownRequiredCheckBox.IsChecked == false &&
                          ActiveRequiredCheckBox.IsChecked == false; 

    // if both are checked, only show active non-shutdown tasks
    bool showActiveNonShutdown = ActiveRequiredCheckBox.IsChecked == true &&
                                 tasks.Active == "YES" &&
                                 NoShutDownRequiredCheckBox.IsChecked == true &&
                                 tasks.ShutdownRequired == "NO";

    // if active is checked but shudown isn't, display all active
    bool showActive = ActiveRequiredCheckBox.IsChecked == true &&
                      tasks.Active == "YES" &&
                      NoShutDownRequiredCheckBox.IsChecked == false;

    // if non-shutdown is checked but active isn't, display all non-shutdown tasks
    bool showNonShutdown = NoShutDownRequiredCheckBox.IsChecked == true &&
                           tasks.ShutdownRequired == "NO" &&
                           ActiveRequiredCheckBox.IsChecked == false;

    return showEverything || showActiveNonShutdown || showActive || showNonShutdown;
}

Заранее спасибо.

Изменить:

Я изменил это на это. назначение локальных переменных для условий флажка не имело никакого эффекта, но создание логических значений из «ДА» / «НЕТ» увеличило сложность до 14, что, я думаю, я понимаю.

public bool FitsCheckBoxCriteria(LubeTask tasks)
{
    bool noShutdownReqChecked = (bool)NoShutDownRequiredCheckBox.IsChecked;
    bool activeChecked = (bool)ActiveRequiredCheckBox.IsChecked;

    bool active = tasks.Active == "YES" ? true : false;
    bool shutdownReq = tasks.ShutdownRequired == "YES" ? true : false;

    // if neither checkboxes are checked, show everything
    bool showEverything = !noShutdownReqChecked && !activeChecked;

    // if both are checked, only show activeChecked non-shutdown tasks
    bool showActiveNonShutdown = activeChecked && noShutdownReqChecked && active && !shutdownReq;

    // if activeChecked is checked but shudown isn't, display all activeChecked
    bool showActive = activeChecked && !noShutdownReqChecked && active;

    // if non-shutdown is chceked but activeChecked isn't, display all non-shutdown tasks
    bool showNonShutdown = noShutdownReqChecked && !activeChecked && !shutdownReq;

    return showEverything || showActiveNonShutdown || showActive || showNonShutdown;
}

person Charles Clayton    schedule 23.04.2015    source источник
comment
Просто любопытно, что произойдет со сложностью, если вы удалите все операторы == true и замените == false на !? они избыточны   -  person moarboilerplate    schedule 24.04.2015
comment
@moarboilerplate Я знаю, что они избыточны. Просто это тип bool?, а не bool, поэтому вместо приведения их всех я просто провел сравнение для удобства чтения. msdn.microsoft.com/en-us/library/bb384091.aspx   -  person Charles Clayton    schedule 24.04.2015
comment
Да, но что это делает со сложностью? Кроме того, если вы заинтересованы в оптимизации этого, я бы использовал перечисление флагов и побитовые операции, чтобы установить разрешение для каждого бита.   -  person moarboilerplate    schedule 24.04.2015
comment
(хотя это убирает обнуляемый аспект вашего логического значения)   -  person moarboilerplate    schedule 24.04.2015
comment
Хм, я избавился от сравнений и сделал приведения, и это не изменило сложности. Хотя это было бы аккуратно.   -  person Charles Clayton    schedule 24.04.2015
comment
Да, поскольку вы сравниваете их с постоянными значениями, компилятор обычно достаточно умен, чтобы понять это:/   -  person moarboilerplate    schedule 24.04.2015


Ответы (3)


Ключ находится в "независимых путях".

Я собираюсь переписать ваш код, чтобы сократить его, чтобы мы могли его обсудить.

public bool FitsCheckBoxCriteria(TaskClass tasks)
{
    bool E1 = A1 && A2; 

    bool E2 = B1 && B2 && B3 && B4;

    bool E3 = C1 && C2 && C3;

    bool E4 = D1 && D2 && D3;

    return E1 || E2 || E3 || E4;
}

Цикломатическая сложность — это количество независимых путей. Это не общее возможное количество возвращаемых значений (2).

Оператор && и || операторные операции короткого замыкания; если A1 ложно, A2 не оценивается. Точно так же, если E1 истинно, E2 не оценивается.

Если вы замените все && на & и все || на | в приведенном выше коде цикломатическая сложность равна 1, потому что в коде есть только один путь. (Хотя это не улучшит код).

Как бы то ни было, существует 72 возможных пути...

  1. A1, B1, C1, D1, E1 оцениваются; остальные нет.
  2. Оцениваются A1, A2, B1, C1, D1, E1; остальные нет.
  3. Оцениваются A1, B1, B2, C1, D1, E1; остальные нет.
  4. Оцениваются A1, A2, B1, B2, C1, D1, E1; остальные нет. так далее...

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

Итак, в этом примере вы можете посчитать код вручную следующим образом:

1 + количество операторов короткого замыкания в этом коде (11) = 12.

В Википедии есть отличное подробное объяснение.

person perfectionist    schedule 27.04.2015

Это всего лишь предположение, но я думаю, что присваивания равны +2 каждое (if =true/else =false), а затем +1 для каждого возможного условия выхода в операторе return. Таким образом, это может превратиться во что-то вроде:

bool showEverything = false;
if (...) { showEverything = true; } +1
else { showEverything = false; } +1

bool showActiveNonShutdown = ... +2 if/else
bool showActive = ... +2 if/else
bool showNonShutdown = ... +2 if/else

if (showEverything) {...} +1
else if (showActiveNonShutdown) {...} +1
else if (showActive) {...} +1
else if (showNonShutdown) {...} +1
else {false}
person doogle    schedule 23.04.2015
comment
Это очень интересный взгляд на это! - person Charles Clayton; 23.04.2015

C# использует оценку с коротким замыканием, что означает, что если есть выражение x && y, то y оценивается только в случае необходимости, точнее, если x истинно. Это означает, что result = x && y; имеет два независимых пути выполнения: (1) если x ложно, тогда оценивается только x, а result получает ложное значение (без оценки y), но (2) если x истинно, y также оценивается, а result получает оценку результат y. Это означает, что каждый оператор && и || увеличивает цикломатическую сложность, а в первом примере 8 && и 3 || операторов, поэтому цикломатическая сложность метода равна 12.

person Community    schedule 26.04.2015