为什么要拒绝嵌套代码?

欢迎访问我的个人博客 造梦网,本文首发于 https://www.zaom.cc/why-you-need-to-reject-nested-code/

linux 内核的最早作者 linus torvalds 在 linux 内核样式指南 第一节中提到:

if you need more than 3 levels of indentation, you’re screwed anyway, and should fix your program.

如果你需要超过3级的缩进,你无论如何都搞砸了,应该修复你的程序。

什么是嵌套

嵌套代码是指您向函数中添加更多的内部块。一般认为,每一个左大括号都为函数增加了一个深度。下面介绍一些例子:

int calculate(int bottom, int top)
{
  return bottom + top;
}

此函数的嵌套深度为 1.

在此基础上再增加一个 if 语句:

int calculate(int bottom, int top)
{
  if (top > bottom)
  {
    return bottom + top;
  }
  else
  {
    return 0;
  }
}

该函数的嵌套深度增加为 2.

如果再增加一个循环:

int calculate(int bottom, int top)
{
  if (top > bottom)
  {
    int sum = 0;
    for (int number = bottom; number <= top; number++)
    {
      sum += number;
    }
    return sum;
  }
  else
  {
    return 0;
  }
}

此时,calculate 函数有三个左大括号,那么我们认为该函数的深度为 3.

许多优秀的工程师都认为,三层嵌套是优秀编程范式的最大深度。

如果超过了三层,那么随之而来的复杂程度将会成倍增加,复杂的函数逻辑将会使阅读和修改变得困难甚至毫无可能。

尤其是在千万行级别的大型工程中,这种毫无制约的嵌套将会是一个定时炸弹!

那么如何重构代码,使其去嵌套化呢?

去嵌套化

我们有两种方式重构代码,使其嵌套深度小于等于 3提取和反转。这两者结合可以达到我们的目的。

提取是指将复杂函数中的特定部分提取出来,使其成为一个新的函数。

反转是指反转 ifelse 中的代码块,使其能够提前 return ,继而去除 else 语句,减小嵌套深度。

首先给出一个四层深度的实例代码,此代码在上述 for 循环中增加了一个 if 语句:

int calculate(int bottom, int top)
{ // level one
  if (top > bottom)
  { // level two
    int sum = 0;
    for (int number = bottom; number <= top; number++)
    { // level three
      if (number % 2 == 0)
      { // level four
        sum += number;
      }
    }
    return sum;
  }
  else
  {
    return 0;
  }
}

提取

for 循环内部部分提取出来,形成一个单独的函数 filterNumber.

int filterNumber(int number)
{
  if (number % 2 == 0)
  {
    return number;
  }
  return 0;
}

int calculate(int bottom, int top)
{ // level one
  if (top > bottom)
  { // level two
    int sum = 0;
    for (int number = bottom; number <= top; number++)
    { // level three
      sum += filterNumber(number);
    }
    return sum;
  }
  else
  { // level two
    return 0;
  }
}

此时的函数深度为 3.

反转

反转的思路就是,将一些错误判定条件(提前 return,或者抛出异常)放到真正的业务代码前面,这样就可以省略了许多的 else 语句,避免陷入更深的嵌套中。

接下来我们反转 ifelse 中的语句,并将 if 中的条件取反:

int filterNumber(int number)
{
  if (number % 2 == 0)
  {
    return number;
  }
  return 0;
}

int calculate(int bottom, int top)
{ // level one
  if (top <= bottom)
  { // level two
    return 0;
  }
  else
  { // level two
    int sum = 0;
    for (int number = bottom; number <= top; number++)
    { // level three
      sum += filterNumber(number);
    }
    return sum;
  }
}

实际上容易发现,我们并不需要这个 else 语句块,因为如果满足那个会导致 提前return的条件,那么语句将会按照正常顺序执行。所以去掉 else

int filterNumber(int number)
{
  if (number % 2 == 0)
  {
    return number;
  }
  return 0;
}

int calculate(int bottom, int top)
{ // level one
  if (top <= bottom)
  { // level two
    return 0;
  }
  int sum = 0;
  for (int number = bottom; number <= top; number++)
  { // level two
    sum += filterNumber(number);
  }
  return sum;
}

此时可以发现,该函数的嵌套深度已经缩小为 2,代码逻辑更加清晰,易读性大大增加。

结语

本文只是举了一个十分简单的例子,在实践中遇到的代码将会更加复杂。

其实万变不离其宗,就是这两种最实用的方法:提取和反转。多次,灵活的运用这两种方法,将会使你的程序更加清晰明了,符合国际主流代码风范。


欢迎关注微信公众号:通用代码技术
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

通用代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值