Как правильно обращаться с логикой «один, оба или нет»? - PullRequest
5 голосов
/ 24 декабря 2010

У меня логическая ситуация, которую лучше всего описать как две «Команды», пытающиеся выиграть задание.Результатом этой задачи может стать одиночный победитель, ничья (ничья) или отсутствие победителя (пат).

В настоящее время я использую вложенное выражение if / else, например:

// using PHP, but the concept seems language agnostic.
if ($team_a->win()) {
    if ($team_b->win()) {
        //  this is a draw
    } else {
        //  team_a is the winner
    }
} else {
    if ($team_b->win()) { 
        //  team_b is the winner
    } else {
        //  This is a stalemate, no winner.
    }
}

Это выглядит довольно спагетти и повторяется.Есть ли более логичный, сухой шаблон, который я мог бы использовать?

Ответы [ 10 ]

6 голосов
/ 24 декабря 2010

Другим способом было бы, если победа (a) && win (b), тогда ничья, иначе, если победа (a), иначе, если победа (b).

Или:

if win(a) and win(b) then
   // Draw
else if win(a) then
   // a wins
else if win(b) then
   // b wins
else 
   // Stalemate
4 голосов
/ 24 декабря 2010

Не думаю, что это можно сделать намного лучше, чем то, что вы делаете в настоящее время.

Одной из альтернатив является использование выражения-переключателя:

switch (($team_a->win() << 1) + $team_b->win()) {
case 3:
    //  this is a draw
case 2:
    //  team_a is the winner
case 1:
    //  team_b is the winner
case 0:
    //  This is a stalemate, no winner.
}

Однако, хотя он более СУХОЙ, я не думаю, что это улучшает читабельность. Обратите внимание, что на некоторых языках вместо $team_x->win() необходимо писать ($team_x->win() ? 1 : 0).

3 голосов
/ 24 декабря 2010
if($TeamA->win() && $TeamB->win()){
  // Tie
}else if($TeamA->win()){
  // Team A wins
}else if($TeamB->win()){
  // Team B wins
}else{
  // No winner
}

Кроме того, в зависимости от того, что делает ваш метод win (), может быть более эффективно проверить его один раз за пределами if...else, поэтому проверка выполняется только один раз:

$team_a = $TeamA->win();
$team_b = $TeamB->win();

if($team_a && $team_b){
  // Tie
}else if($team_a){
  // Team A wins
}else if($team_b){
  // Team B wins
}else{
  // No winner
}
2 голосов
/ 24 декабря 2010

, чтобы упростить ваши вложенные операторы:

if($team_a->win() and $team_b->win())
{
  // Draw
}
elseif($team_a->win())
}
  // Team A Won
}
elseif($team_b->win())
}
  // Team B Won
}
else
{
  // No Winner
}
1 голос
/ 24 декабря 2010

Когда логика этого типа заключена в методе, ранние возвраты могут быть более читабельными, чем если бы {} else блокировалось:

function game_end($a,$b) {
    if ($team_a->win() && $team_b->win()) {
            //  this is a draw
            return;
    } 

    if ($team_a->win()) {
            //  team_a is the winner
            return ;
    }

    if ($team_b->win()) {
            //  team_b is the winner
            return ;
    }

    //  This is a stalemate, no winner.

}
1 голос
/ 24 декабря 2010

Вот расширенная версия версии @Mark Byers, нацеленная на то, чтобы сделать логику более понятной.

$result_code = 0;
if ($team_a->win()) $result_code += 1;
if ($team_b->win()) $result_code += 2;
switch ($result_code) {
   case 0:
      //stalemate
   case 1:
      //a wins
   case 2:
      //b wins
   case 3:
      //draw
}

Это очень хорошая процедура, одна из цифр, которая не складываетсятот же результат в любой комбинации, и я думаю, у вас будет много поводов использовать его в будущем.Это та же логика, которая используется в разрешениях * nix, и может работать также с <2 операндами: если бы их было три, вы могли бы использовать 4, 2 и 1. и т. Д.обработка, так как вы вызываете методы командных объектов только один раз. </p>

0 голосов
/ 03 февраля 2011

Вопрос о поиске СУХОГО паттерна здесь неправильный, поскольку проблема не является достаточно сложной, чтобы оправдать рефакторинг / упрощение, а ее цикломатическая сложность слишком мала, чтобы беспокоиться о спагеттизации .

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

IF (A ^ B) -> draw
ELSE (!A ^ !B) -> no win
ELSE A -> a wins
ELSE -> b wins

Невозможно сделать логику с чем-то меньшим. Единственная избыточность здесь - это пересчет $team_b->win() (просто вычислите выигрыш для обеих команд и поместите их в локальные переменные перед оператором if).

0 голосов
/ 28 декабря 2010

Как я уже сказал в комментарии, я думаю, что условие победы и его определение не в том месте. Что не так (пример быстрого электронного кода):

if ($team_a->score() > $team_b->score()) return 'Team A wins!';
else if ($team_a->score() < $team_b->score()) return 'Team B wins!';
else return 'It's a tie!';

(синтаксис может быть неправильным, но вы поняли)

Лучше провести рефакторинг, чем пытаться найти решение проблемы, которую вы создали сами.

0 голосов
/ 24 декабря 2010

Если вы собираетесь разделиться на две группы по две, подумайте:

if (win(a) != win(b)) {
    // common code when there's a winner, regardless of who
    if (win(a)) {
        // code specific to a winning
    } else {
        // code specific to b winning
    }
} else {
    // common code for a non-result
    if (win(a)) {
        // code specific to a draw
    } else {
        // code specific to a stalemate
    }
}

Может быть полезно, если вы находитесь в наилучшем месте, где в одной из двух позиций есть какой-то общий кодно не стоит или не возможно абстрагироваться в функцию и вызывать ее из двух из четырех блоков в ответах if/else if и switch/case.

0 голосов
/ 24 декабря 2010

Разве вы не можете использовать более прямое утверждение ...

что-то вроде

IF(a AND b) THEN {a,b}
ELSE If (a AND NOT(b)) THEN {a}
ELSE If (not(a) AND NOT(b)) THEN { }
ELSE {b}

, в отличие от некоторых других постов, это очень читабельное, но не самое краткое.

...