代码健康(9):减少嵌套, 减少复杂性

乔梁 | 2021-03-09

嵌套过深的代码对可读性有害,而且容易出错。

而通过卫语句来检查某一条件,如果不满足就直接快速失败。

卫语句将计算逻辑与错误逻辑分离。通过消除错误检查和错误处理之间的认知距离,它释放了工程师的心算负担。

请在下面两个版本的代码中试着找一找bug

response = server.Call(request)
if response.GetStatus() == RPC.OK:
    if response.GetAuthorizedUser():
        if response.GetEnc() == 'utf-8':
            if response.GetRows():
                vals = [ParseRow(r) for r in response.GetRows()]
                avg = sum(vals) / len(vals)
                return avg, vals
            else:
                raise EmptyError()
        else:
            raise AuthError('unauthorized')
    else:
        raise ValueError('wrong encoding')
else:
    raise RpcError(response.GetStatus())
response = server.Call(request)
if response.GetStatus() != RPC.OK:
    raise RpcError(response.GetStatus())
if not response.GetAuthorizedUser():
    raise ValueError('wrong encoding')
if response.GetEnc() != 'utf-8':
    raise AuthError('unauthorized')
if not response.GetRows():
    raise EmptyError()
vals = [ParseRow(r) for r in response.GetRows()]
avg = sum(vals) / len(vals)
return avg, vals

答案

wrong encodingunauthorized 这两个错误信息写反了。

上面展示的重构技术就是 “卫语句” 。所以,**重构后的版本更容易读懂,也更容易维护。**重构以后,这个 bug 更容易被发现,因为,在代码中直接进行检查,检查后马上就处理错误信息。

下面是一些减少嵌套代码的原则:

  • 尽量让条件语句块短小。将内容限制在局部,有利于增加可读性。
  • 当循环或分支超过两层深度时,就要考虑重构它了。
  • 可以将嵌套逻辑抽取到另一个函数中。假如你需要从一个列表中取出每一个元素,而每个元素又都是一个列表(例如,一个协议缓冲区中还有重复的字段),你就可以定义一个函数来处理每个对象,而不是使用双嵌套循环。

减少嵌套会使代码更易读,从而更容易发现可能存在的错误,更快的开发迭代,以及更高的稳定性。只要允许,就进行简化!