如何减少IF语句的数量?

时间:2012-10-30 12:26:13

标签: php class if-statement refactoring dry

我有很多IF句子,每个句子都有一个功能 是否有一种明显的方法可以更简单地编写此代码? 每个IF都会启动不同的功能,但它看起来仍然有点矫枉过正。

    if ($this->machine == '' AND $this->date_from == '' AND $this->date_to == '' AND $this->date_like == '' AND $this->article_or_tool == '') {
        $this->AllTime();
    }
    if ($this->machine <> 0 AND $this->date_from == '' AND $this->date_to == '' AND $this->date_like == '' AND $this->article_or_tool == '') {
        $this->ByMachine();
    }
    if ($this->machine == '' AND $this->date_from <> 0 AND $this->date_to <> 0 AND $this->date_like == '' AND $this->article_or_tool == '') {
        $this->ByDate();
    }
    if ($this->machine <> 0 AND $this->date_from <> 0 AND $this->date_to <> 0 AND $this->date_like == '' AND $this->article_or_tool == '') {
        $this->ByMachineByDate();
    }
    if ($this->machine == '' AND $this->date_from == '' AND $this->date_to == '' AND $this->date_like <> 0 AND $this->article_or_tool == '') {
        $this->ByDateLike();
    }
    if ($this->machine <> 0 AND $this->date_from == '' AND $this->date_to == '' AND $this->date_like <> 0 AND $this->article_or_tool == '') {
        $this->ByMachineByDateLike();
    }
    if ($this->machine == '' AND $this->date_from == '' AND $this->date_to == '' AND $this->date_like == '' AND $this->article_or_tool <> 0) {
        $this->ByArticle();
    }
    if ($this->machine <> 0 AND $this->date_from == '' AND $this->date_to == '' AND $this->date_like == '' AND $this->article_or_tool <> 0) {
        $this->ByMachineByArticle();
    }
    if ($this->machine == '' AND $this->date_from <> 0 AND $this->date_to <> 0 AND $this->date_like == '' AND $this->article_or_tool <> 0) {
        $this->ByDateByArticle();
    }
    if ($this->machine == '' AND $this->date_from == '' AND $this->date_to == '' AND $this->date_like <> 0 AND $this->article_or_tool <> 0) {
        $this->ByDateLikeByArticle();
    }
    if ($this->machine <> 0 AND $this->date_from <> 0 AND $this->date_to <> 0 AND $this->date_like == '' AND $this->article_or_tool <> 0) {
        $this->ByMachineByDateByArticle();
    }
    if ($this->machine <> 0 AND $this->date_from == '' AND $this->date_to == '' AND $this->date_like <> 0 AND $this->article_or_tool <> 0) {
        $this->ByMachineByDateLikeByArticle();
    }


以下是重构后的代码:

function MethodPicker() {
    $machine            = $this->machine            <> 0;
    $date_from          = $this->date_from          <> 0;
    $date_to            = $this->date_to            <> 0;
    $date_like          = $this->date_like          <> 0;
    $article_or_tool    = $this->article_or_tool    <> 0;

    $decision  = array($machine, $date_from, $date_to, $date_like, $article_or_tool);
    $decisions = array(
                    'AllTime' =>                        array(false,    false,  false,  false,  false   ),
                    'ByMachine' =>                      array(true,     false,  false,  false,  false   ),
                    'ByDate' =>                         array(false,    true,   true,   false,  false   ),
                    'ByMachineByDate' =>                array(true,     true,   true,   false,  false   ),
                    'ByDateLike' =>                     array(false,    false,  false,  true,   false   ),
                    'ByMachineByDateLike' =>            array(true,     false,  false,  true,   false   ),
                    'ByArticle' =>                      array(false,    false,  false,  false,  true    ),
                    'ByMachineByArticle' =>             array(true,     false,  false,  false,  true    ),
                    'ByDateByArticle' =>                array(false,    true,   true,   false,  true    ),
                    'ByDateLikeByArticle' =>            array(false,    false,  false,  true,   true    ),
                    'ByMachineByDateByArticle' =>       array(true,     true,   true,   false,  true    ),
                    'ByMachineByDateLikeByArticle' =>   array(true,     false,  false,  true,   true    ),
    );
    $method = array_keys($decisions, $decision, true);
    $method && list($method) = $method;
    $method && $this->$method();
}

5 个答案:

答案 0 :(得分:2)

也许你真的需要所有这些决定,但你可以让它更容易阅读。您可以先将此值设置为有意义的变量,而不是在if语句中多次写入$this->machine == ''

$machineIsEmpty = $this->machine == '' || $this->machine == 0;
$dateFromIsEmpty = $this->date_from == '' || $this->machine == 0;
...

if ($machineIsEmpty && $dateFromIsEmpty && $dateToIsEmpty && $dateLikeIsEmpty && $articleOrToolIsEmpty)
{
  $this->AllTime();
}
else if (!$machineIsEmpty && $dateFromIsEmpty && $dateToIsEmpty && $dateLikeIsEmpty && $articleOrToolIsEmpty)
{
  $this->ByMachine();
}
...

在这个例子中,我假设有两件事:首先我怀疑你想要将值''0都处理为未设置。我不确定这一点,因为没有任何情况下您使用值0做了任何事情。

其次我假设如果调用了一个函数,你不想再调用其他函数,所以我在下一个if之前添加了一个else。

在我看来,嵌套if语句会使代码更难以阅读 ,因为你必须记住你当前在if语句的哪个级别。


另一种方法是使用决策矩阵。您可以编写一个包含所有可能组合的数组,每个组合都知道函数名称。

$myObject = new TestClass();
$myObject->DoAction($machineIsSet, $dateFromIsSet, $dateToIsSet, $dateLikeIsSet, $articleToolIsSet);

class TestClass
{
  private $actionMatrix = array(
    //    machine, dateFrom, dateTo, dateLike, articleOrTool, action
    array(false,   false,    false,  false,    false,         'AllTime'),
    array(true,    false,    false,  false,    false,         'ByMachine')
  );

  public function DoAction($machine, $dateFrom, $dateTo, $dateLike, $articleOrTool)
  {
    foreach($this->actionMatrix as $action)
    {
      if (($action[0] == $machine) && ($action[1] == $dateFrom) && ($action[2] == $dateTo) && ($action[3] == $dateLike) && ($action[4] == $articleOrToolLike))
      {
        $functionName = $action[5];
        $this->$functionName(); // call the function
        break;
      }
    }
    // no action found, maybe we want some error handling here?
  }

  public function AllTime()
  {
    echo('AllTime');
  }

  public function ByMachine()
  {
    echo('ByMachine');
  }
}

答案 1 :(得分:2)

首先,我会做一些标准的重构。不知道为什么我这样做,但这是什么:

  1. 使用局部变量替换属性,例如

    $machine = $this->machine;
    
  2. 同样适用于条件但是更接近条件时,很明显只有每个变量有两个状态,所以这实际上只是每个变量的一个条件(参见Type Juggling)在truefalse中。相反,分配条件:

    $machine = $this->machine == '' || $this->machine == 0;
    
  3. 积分转到martinstoeckli了解正确条件

    这将是一个开始。 if条款到现在为止已经改变了,并且会更加紧凑。但是,为何停在这里?目前有一个决定:

    $decision  = [$machine, $date_from, $date_to, $date_like, $article_or_tool];
    

    有一系列决定可供选择:

    $decisions = [
        'AllTime' => [true, true, true, true, true],
        ...
    ];
    

    所以需要做的就是找到决定并执行方法:

    $method = array_keys($decisions, $decision, true);
    $method && $this->$method();
    

    if块已变为矩阵。该函数已映射到它的一个状态。

    你松开了字段上的名字,但是,你可以通过评论来解决这个问题:

        $decisions = [
            //            machine  from  to    like  article
            'AllTime' => [true   , true, true, true, true],
            ...
        ];
    

    一目了然:

    $machine = $this->machine == '' || $this->machine == 0;
    ... # 4 more times
    
    $decision  = [$machine, $date_from, $date_to, $date_like, $article_or_tool];
    
    $decisions = [
        'AllTime' => [true, true, true, true, true],
        ... # 11 more times
    ];
    
    $method = array_keys($decisions, $decision, true);
    $method && $this->$method();
    

    如果这个类代表一个值对象,我建议你将决策移动到它自己的类型中,然后将该决策类型用作单个方法对象。将使您以后能够更轻松地做出不同的决策。

答案 2 :(得分:1)

很多都是重复,所以你可以嵌套条件来删除冗余,例如:

if ($this->machine == '') {
  // do everything requiring empty 'machine' string
  // remove condition from all subsequent ifs in this context
} else if ($this-> machine <> 0) {

}

我没有时间或倾向于完成所有代码并实际为您执行此操作,但这是一个想法,应该为您提供足够的信息来实现作为读者的练习。 (:

答案 3 :(得分:0)

有时候,如果您的条件不同,则不可能,但在这种情况下,您可以从分组if()开始,因为许多人都有相同的条件。例如,而不是吨吨

if( $this->machine <> 0 AND .... )

你可以将它们组合在一起:

if( $this->machine <> 0 ) {
    // all related ifs there
}

然后继续下一个“级别”的条件。虽然这可能不会减少if()的总数,但您的代码将比现在更具可读性。

答案 4 :(得分:0)

正如我所见,你有一个约定你的方法的约定。 如果我有这样的代码和这样的约定,我会使用调用方法按名称重构它,这样你的代码将非常简短,易于阅读和维护

   $method = '';

    if (this->machine <> 0) $method.="ByMachine";
    if ($this->date_from <> 0 AND $this->date_to <> 0) $method.="ByDate"
    .....
    //here you have full $method name ByMachineByDateByArticle or ByMachineByDateByArticle etc
    if($method){ 
       call_user_func_array(array($this, $method));
    }