Clean Code(减少不必要的嵌套)

任何一个傻瓜都能写出计算机可以理解的代码,惟有写出人类容易理解的代码,才是优秀的程序员
《重构 改善既有代码的设计》

今天在看团队的代码,看到了一些不好的,也学习了一些值得借鉴的地方,就记录一下,作为分享

减少嵌套

减少没必要的嵌套,因为嵌套太深,真的读起来,读到最里面的,然后忘记外面的逻辑了。举一个非常经典的反例。

这是一个很经典的登录验证的例子。按照正常的思维进去,先判断是不是post 然后判断用户名是不是空,密码是不是空的。但是这样就带来一个问题。代码嵌套的非常深。

if($_SERVER['REQUEST_METHOD'] == 'POST')  {
    if(isset($_POST['username'])) {
        if(!empty($_POST['username'])){
            if(isset($_POST['password'])){
                if(!empty($_POST['password'])) {
                    $username = $_POST['username'];
                    $password = $_POST['password'];
                }else{
                    die("密码不能为空");
                }
            }else{
                die("密码没填写");
            }
        }else{

        }
    }else{

    }
}else{

}

1. 代码尽量提前返回/结束

上面的代码我们反向进行思考。如果不是post 就退出,下面的只执行post的逻辑.修改后如下

if($_SERVER['REQUEST_METHOD'] !== 'POST')  {
    die("403");
}
if(!isset($_POST['username']) || empty($_POST['username'])) {
    die("用户名不能为空");
}
if(!isset($_POST['password'])|| empty($_POST['password'])) {
    die("密码不能为空");
}
$username = $_POST['username'];
$password = $_POST['password'];

还是以上面的代码为例子

function check() {
    $msg = "";
    if($_SERVER['REQUEST_METHOD'] !== 'POST')  {
        $msg = "禁止访问"
    }
    if(!isset($_POST['username']) || empty($_POST['username'])) {
        $msg = "用户名不能为空";
    }
    if(!isset($_POST['password'])|| empty($_POST['password'])) {
        $msg = "密码不能为空";
    }
    return $msg;
}

但是的代码可以提前返回返回错误信息。这样在debug的时候,到return的时候,就会提前结束掉。不然如果代码很长。下面的代码还需要走。

function check() {
    $msg = "";
    if($_SERVER['REQUEST_METHOD'] !== 'POST')  {
        return  "禁止访问"
    }
    if(!isset($_POST['username']) || empty($_POST['username'])) {
        return "用户名不能为空";
    }
    if(!isset($_POST['password'])|| empty($_POST['password'])) {
       return "密码不能为空";
    }
    return $msg;
}

2. 减少不必要的else分支【非此即彼】

我们在学习if else 的时候,都知道 不是 if 就是else 但是有时候 else 并不是必要的、在我们需要做变量赋值的时候,非此即彼的选择的选择,可以通过设置默认值,然后来减少掉else分支。或者通过三元运算符来写更简洁的代码

#bad
$a = 10;
if($a %2 == 0) {
    echo "a是偶数"
}else{
    echo "a是奇数"
}

function isOld($num) {
    if($num %2 == 0) {
        return "a是偶数"
    }else{
        return "a是奇数"
    }
}

我们可以减少else分支

$a = 10;
$string = "a是奇数";
if($a % 2 == 0) {
    $string = "a是偶数";
}
echo $string;

$string = ($a %2 == 0) ? "a是偶数" :"a是奇数 ";

3. foreach中使用break、continue

foreach 是我们用的最多的一种遍历。当遍历数组的时候,我们经常会在foreach里面进行判断。但是我们尽量能实用continue 或者break。跳出或跳过该记录。尽量在if的逻辑中,不要太多的代码。

$data = [1,2,4,5,6];
foreach($data as $item) {
    if($item === 10) {
        //业务逻辑

        break;
    }
}

foreach($data as $item) {
    if($item !== 10) {
       continue;
    }
    //业务逻辑
}

4. 尽量不使用switch

switch 语句有时候容易漏写 一个 break 导致意想不到的错误

switch ($variable) {
    case 'a':
        $num = 1;
        break;
    case 'b':
        $num = 2;
        break;

    default:
        $num = 1;
        break;
}

我们通过一个数组映射的关系来简写上面的代码结构

$map  = [
    'a'=>1,
    'b'=>2
];
$num = $map[$variable] ?? 1;

5. 实用常量来标记一些类型值

我们在开发中,经常的用到一些数字来表示状态,比如订单的状态,请求的状态,但是数字有时候不是特别容易一眼就看懂,有些人喜欢用1 表示成功, 有些人用1 表示失败。所以就容易混淆,我们可以通过常量来定义这些值。我们见名识义,看到这个名字,就知道代表啥状态,而不是再去看那些数字

class Test
{
    const SUCCESSS = 1;
    const ERROR  = 0;

    public function index()
    {
        if(1) {
            return ['code'=>static::SUCCESSS,'data'=>[]];
        }
        return ['code'=>static::ERROR,'data'=>[]];
    }

}

总结

最后简单的总结一下

  1. 如果遇到非此即彼的条件的时候,可以用默认值或提前返回,来较少没必要的else 分支
  2. foreach 中 如果有if条件的时候 可以用continuebreak 跳过记录。来减少if 中的代码量
  3. switch 语句可以改成 map映射的关系对。

如果有不对的地方,欢迎指出,感谢!

闲云野鹤

《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 3
gangpula

if(!isset($_POST['username']) || empty($_POST['username'])) {
    die("用户名不能为空");
}

用empty就够了,包含了

5个月前
pigzzz

野鸽大佬你咋不在群里说泡了

5个月前
xianyunyehe

@gangpula 两个函数的用法不一样。 不如不传username 会直接报notice警告

5个月前

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!