Как я могу уменьшить цикломатическую сложность функции, которая возвращает значение, зависящее от декартового произведения 3 логических значений?

Как я могу уменьшить цикломатическую сложность функции, которая возвращает значение, зависящее от декартового произведения 3 логических значений? Как я могу сделать следующий код более чистым?

Это для школьного проекта и не является обязательным требованием для самого задания, но я обычно нахожу себя пишущим функции, которые полагаются на — иногда — довольно сложную таблицу истинности. Я думаю, что это не может быть лучшим способом сделать это.

    public function getDiscount( $values ) {

$res       = new stdClass();
$res->code = 400;

if ( ! is_bool( $values['new_customer'] ) || ! is_bool( $values['loyalty_card'] ) || ! is_bool( $values['coupon'] ) ) {
$res->data = "Missing inputs";

return $res;
}

if ( $values['new_customer'] == true && $values['loyalty_card'] == true && $values['coupon'] == true ) {
$res->data = "Invalid input";

return $res;
}

if ( $values['new_customer'] == true && $values['loyalty_card'] == true && $values['coupon'] == false ) {
$res->data = "Invalid input";

return $res;
}

$res->code = 200;

if ( $values['new_customer'] == true && $values['loyalty_card'] == false && $values['coupon'] == true ) {
$res->data = 20;

return $res;
}

if ( $values['new_customer'] == true && $values['loyalty_card'] == false && $values['coupon'] == false ) {
$res->data = 15;

return $res;
}

if ( $values['new_customer'] == false && $values['loyalty_card'] == true && $values['coupon'] == true ) {
$res->data = 30;

return $res;
}

if ( $values['new_customer'] == false && $values['loyalty_card'] == true && $values['coupon'] == false ) {
$res->data = 10;

return $res;
}

if ( $values['new_customer'] == false && $values['loyalty_card'] == false && $values['coupon'] == true ) {
$res->data = 20;

return $res;
}

if ( $values['new_customer'] == false && $values['loyalty_card'] == false && $values['coupon'] == false ) {
$res->data = 0;

return $res;
}

$res->code = 400;
$res->data = "Invalid input";
return $res;

}

2

Решение

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

Мой Фаворит — это новое сопоставление с образцом в операторах переключения C # 7.0 https://visualstudiomagazine.com/articles/2017/02/01/pattern-matching.aspx

Перво-наперво, определите вашу таблицу истинности

CASE    New Customer    Loyalty     Coupon  Output
1        Yes            Yes        Yes    'Invalid Input'
2        Yes            Yes        No     'Invalid Input'
3        Yes            No         Yes      20
4        Yes            No         No       15
5        No             Yes        Yes      30
6        No             Yes        No       10
7        No             No         Yes      20
8        No             No         No       0

Сразу видно, что случаи 1 и 2 не зависят от условия купона, так что это ваша первая оптимизация.
Второе условие, на которое следует обратить внимание, состоит в том, что не бывает случаев, когда новый клиент является истинным, и у него есть карта лояльности, это имеет смысл.
Отсюда проще всего визуально следовать в коде и программировать — использовать вложенное ветвление, таким образом, мы проверяем каждую проверку условий только один раз.

public function getDiscount( $values ) {

$res       = new stdClass();
$res->code = 400;

if ( ! is_bool( $values['new_customer'] ) || ! is_bool( $values['loyalty_card'] ) || ! is_bool( $values['coupon'] ) ) {
$res->data = "Missing inputs";

return $res;
}

if ( $values['new_customer'] == true && $values['loyalty_card'] == true ) {
$res->data = "Invalid input";

return $res;
}

$res->code = 200;

// Check New Customer conditions
if ( $values['new_customer'] == true) {
if( $values['coupon'] == true )
$res->data = 20;
else
$res->data = 15;
}

// Check existing customer conditions
else {

// Has Loyalty Card
if ( $values['loyalty_card'] == true ) {
// Has Coupon
if( $values['coupon'] == true )
$res->data = 30;
else
$res->data = 10;
}

// No Loyalty Card
else {
// Has Coupon
if( $values['coupon'] == true )
$res->data = 20;
else
$res->data = 0;
}
}

// Don't need a default fail condition here, because we have covered all possible combinations
return $res;

}

Возможно, было бы проще использовать добавление значений, таблица истинности — хороший способ представить следующий блок кода:

CASE    New Customer    Loyalty     Coupon  Output
1        Yes            Yes        Yes    'Invalid Input'
2        Yes            Yes        No     'Invalid Input'
3        Yes  +15       No         Yes +5   =20
4        Yes  +15       No         No       =15
5        No             Yes +10    Yes +20  =30
6        No             Yes +10    No       =10
7        No             No         Yes +20  =20
8        No             No         No       0

public function getDiscount( $values ) {

$res       = new stdClass();
$res->code = 400;

if ( ! is_bool( $values['new_customer'] ) || ! is_bool( $values['loyalty_card'] ) || ! is_bool( $values['coupon'] ) ) {
$res->data = "Missing inputs";

return $res;
}

if ( $values['new_customer'] == true && $values['loyalty_card'] == true ) {
$res->data = "Invalid input";

return $res;
}

$res->code = 200;
$res->data = 0;

// Check New Customer conditions
if ( $values['new_customer'] == true) {
$res->data += 15;
if( $values['coupon'] == true )
$res->data += 5;
}

// Check existing customer conditions
else {

// Has Loyalty Card
if ( $values['loyalty_card'] == true )
$res->data += 10;

// Has Coupon
if( $values['coupon'] == true )
$res->data += 20;
}

// Don't need a default fail condition here, because we have covered all possible combinations
return $res;

}

Есть много способов обрезать эту кошку 🙂 В приведенных выше примерах кода показано, как можно использовать логику ветвления для оценки каждого условия только один раз. Хотя в этом сценарии это тривиально, оценка некоторых условий в будущем может привести к серьезным последствиям для производительности, поэтому вы можете оценить ее только один раз.

2

Другие решения

Других решений пока нет …