优化分支结构,简化代码,提高可读性

好久没有写文了,毕竟人是越来越懒的。

之前写过一文,说的是 减少if-else的滥用,提高代码可读性。里面提到了用if-return、表驱动等多种方法来代替if-else,可是具体该怎么用以及改进的方式并不是那些清晰。今天说的这个其实还是一样的,不过在此基础上,又添加了一些内容。

注意:此文所阐述的内容仅为个人意见,仅供参考。此文所提供的例子仅作示范使用,不针对任何人。认为胡说八道的,建议右上角

先来看一组代码节选:


class zipFile

{

  protected $zip;

  protected $root;

  protected $ignored_names;

  public function __construct()
  {

    $this->zip = new ZipArchive;
  }

public function unzip($zipfile, $path)
  {

    if ($this->zip->open($zipfile) === true) { 
        //能否打开该压缩文件
      $file_tmp = @fopen($zipfile, "rb");
      $bin = fread($file_tmp, 15); 
      //读取文件头,15字节
      fclose($file_tmp);
      //关闭该文件
      if (true === $this->getTypeList($bin)) {
          //判断是否为压缩文件,只对压缩文件进行处理
        $result = $this->zip->extractTo($path);
        //开始解压,成功返回true
        $this->zip->close();
        //关闭引用
        return $result;
        //返回结果
      } else {
        return false;
        //不是压缩文件,返回false
      }
    }
    return false;
    //不能打开压缩文件,返回false;
  }
  ....etc...
}

从名字上可以看的出来,这个代码片段是一个非常简单的利用php来实现解压文件的的class,这里有一个公共方法unzip,就是具体实现文件解压功能。通读一下实现的步骤:
实现步骤

有什么问题?

当我提出这个问题的时候,肯定有人会说:“这能有什么问题,逻辑很清楚,跑一下代码也跑得出来,没什么问题”。是的,逻辑很清楚,可是,恰恰是逻辑上出了一些小问题。第一个判断那里,如果能打开文件,说明该文件一定是压缩文件,那么第二个判断就是没有必要的。再来,从代码上面看,使用if-return确实也算比较清晰。那么,“问题在哪里呢?”,或者说,“真的存在问题么?”。假设代码逻辑是正确的,也就是默认两个判断都是有效判断,除开这个,还有问题吗?
还有!

    if ($this->zip->open($zipfile) === true) { 
        //能否打开该压缩文件
        //...etc...
      if (true === $this->getTypeList($bin)) {
          //判断是否为压缩文件,只对压缩文件进行处理
        //...etc...
        return $result;
        //返回结果
      } else {
        return false;
        //不是压缩文件,返回false
      }
    }
    return false;

再来一个更加精简的版本

if(){
    if () {
        return Boolean($result);
    }
    else {
        return false
    }
}
return false

看出来问题了吗?我想应该是看出来了。if-returnif-else是可以独立使用的,也就是说,在使用了 if-return 的情况下, if-else是没有必要的。换句话说,上面这个代码可以这样简化

if(){
    if () {
        return Boolean($result);
    }
    //else:  other content here 
    //...etc...
    return false;
}
return false

这样就算结束了?并没有!还可以简化

if(){
    if () {
        return Boolean($result);
    }
    //else:  other content here 
    //...etc...
}
return false

这个时候来比较一下两段代码的逻辑
逻辑对比
从这张图中,可以看出使用if-return确实有助于简化逻辑,不过简化的量得看写代码的人如何去书写。说白了,就是代码习惯的问题。上面这个例子,看上去可读性也很不错了。
按道理来说,你以为到这里差不多就结束了。可是这还并没有到"优化分支结构"这个地步。
事实上,if-return除了简化代码外,还有一个好处就是“优化分支结构”或者叫做调整分支结构
影响代码可读性的,大多就是因为你在无尽的滥用大括号,无尽的嵌套各种分支结构

所以利用if-return优化好分支结构,也是一个很好的方式。将代码进行如下改写:

public function unzip($zipfile, $path)
{

    if ($this->zip->open($zipfile) === false) { 
        return false;
    }
    
    $file_tmp = @fopen($zipfile, "rb");
    $bin = fread($file_tmp, 15); 
    fclose($file_tmp);
    
    if (true === $this->getTypeList($bin)) {
        return false;
    }
    
    $result = $this->zip->extractTo($path);
    $this->zip->close();
    return $result;
}

从上面这个代码片段可以看出,其实多个代码嵌套的分支结构,是可以合理的利用if-return转换为平行(顺序)结构的,平行结构强调的就是从上到下,自然而然。比起无尽的嵌套,减少了很多不必要步骤,读下来,可能更加一目了然。这种思想源于“及时止损”,数学里面有个最不利原则,优先考虑最差的情况,然后逐步筛选出期望达到的目的。当然,不同的人有不同的书写习惯和风格,没有必要为了简化步骤而简化步骤。

个人认为,php代码要写出较高质量,还是要从“简、并、雅”出发。

是简化逻辑(也即简化代码)、简一功能(简单且功能单一,即高内聚低耦合)、简明注释(简单扼要的描述目的、参数、返回等信息)

是代码风格统一、注释与功能统一、具备一定前瞻性(预见未来变化)

是代码符合规范(特定或约定的代码书写风格标准),代码具有较高三性(可读性可维护性可写性)

可能你会有以下问题:

  1. 循环里面怎么用if-return?
    如果你只是取到值就跑,用if-return,如果你想取完所有的值才跑,那应该换成if-continue,并且总是考虑两种情况中,情况最简单的那种(能够尽快返回值或跳出循环)。
  2. 多分支结构怎么办?if-elseif-elseif-else?
    Nope~ 如果你想保持多分支结构,避免if-else方式,采用switch,如果你想转换为平行结构,则可以采用表驱动,并不是说一定要用分离思想,表驱动也是很好的处理多分支的方式。
  3. 我认为你说的是在放p。/我就是喜欢各种嵌套,怎么滴啦?
    右上角。
  4. 分支结构和顺序结构如何选择?
    看你喜欢和个人风格。不是强制性的。不过,虽然高质量的代码不一定是平直的,但是绝对不是s型的。