《编写可读代码的艺术》阅读笔记(二)

简化循环和逻辑

一、流程控制

1. 判断

1)条件判断中的参数顺序

比较的左侧 比较的右侧
“被问询的”表达式,更倾向于不断变化 用来做比较的表达式,它的值更倾向于常量

PS:但是反着写,能够避免掉那种if(obj = NULL)的错误,俗称“尤达表示法”。

2)if/else语句块的顺序

三个倾向性:

  1. 倾向于先处理正逻辑,再处理负逻辑
  2. 倾向于先处理掉简单情况
  3. 倾向于先处理掉有趣的或者可疑的状况

3)?:条件表达式

  • 并不是很推荐这种写法哈哈哈哈,一旦写了长表达式,就会很难理解

2. 循环

1)避免使用do/while循环

通常,逻辑条件应该出现在它们所“保护”的代码前。而且do/while循环中的continue会产生歧义。

2)最小化嵌套

深嵌套会让读者的“思维栈”太深,可以通过提早返回来减少嵌套,可以将代码进行如下变化:

if(a){
if(b){
// do something 1
return
}
// do something 2
}
// do something 3
return

if(!a){
// do something 3
return
}
if(b){
// do something 1
return
}
// do something 2
return

二、长表达式拆分

  • 方法一:增加变量:总结为一句话,用额外的变量,来代替一个长表达式,例子:

    // 解释变量
    if(line.split(':')[0].strip() == 'root'){
    // ...
    }
    // **************************************
    // ↓
    // **************************************
    var userName = line.split(':')[0].strip()
    if(username == 'root'){
    // ...
    }
    // 总结变量
    if(request.user.id == document.owner_id){
    // ...
    }
    // ...
    if(request.user.id != document.owner_id){
    // ...
    }
    // ***********************************************************
    // ↓
    // ************************************************************
    var user_owns_document = (request.user.id == document.owner_id)
    if(user_owns_document) {
    // ...
    }
    if(!user_owns_document) {
    // ...
    }
  • 方法二:使用德摩根定律

    • !(a || b || c) <=> (!a) && (!b) && (!c)
    • !(a && b && c) <=> (!a) || (!b) || (!c)
  • 方法三:不要滥用短路逻辑:短路逻辑可能会增加代码的理解难度。

  • 方法四:找到更优雅的方式,一个理念是:

    开始还很简单的问题变得非常令人费解,通常预示着一中更简单的方法。

  • 方法五:拆分巨大的语句,把一样的表达式抽离出来作为函数开头的总结变量

    • DRY:Don’t Repeat Yourself.

三、变量

关于变量的三个问题:

  • 变量越多,就越难全部追踪
  • 变量作用域越大,就需要跟踪的越久
  • 变量改变越频繁,就越难追踪它的当前值

1)减少变量

针对第一个问题,我们提出了以下几种方案,可以去掉以下几种不必要的变量:

  • 没有价值的临时变量:举个例子:now = datetime.datetime.now(),一般这种变量有以下几种特点,

    • 没有拆分任何临时变量
    • 没有做过多澄清,datetime.now()已经足够清晰
    • 只用一次(或少数几次),没有压缩任何冗余的代码
  • 中间结果:下面例子中,index_to_remove可以被移除,那些为了保存临时结果,用完即丢的变量,可以考虑丢弃:

    var remove_one = function(array, value_to_remove){
    var index_to_remove = null
    for(var i = 0; i < array.length; i++){
    if(array[i] == value_to_remove){
    index_to_remove = i
    break
    }
    }
    if(index_to_remove !== null){
    array.splice(index_to_remove, 1)
    }
    }
    // ***********************************************
    // ↓
    // ***********************************************
    var remove_one = function(array, value_to_remove){
    var index_to_remove = null
    for(var i = 0; i < array.length; i++){
    if(array[i] == value_to_remove){
    array.splice(i, 1)
    return
    }
    }
    }
  • 流程控制变量:用来控制流程的变量,往往不包含任何程序内的数据,能通过结构化编程来消除。

2)缩小变量作用域

让你的变量对尽量少的代码行可见。

  • 一个做法就是让变量“降级”,尽量降低作用域,比如

    • 类成员变量降级成某个成员函数中的局部变量
    • 运用好C++的块级作用域,那些仅仅用来当做判断条件的变量,可以放在if语句中定义:

      PaymentInfo* info = database.ReadPaymentInfo()
      if(info){
      // ...
      }
      // ************************************************
      // ↓
      // ************************************************
      if(PaymentInfo* info = database.ReadPaymentInfo()){
      // ...
      }
    • JavaScript的立即执行函数制造封闭的作用域

  • 定义下移:将定义移动到使用它之前,增加可读性

3)只写一次的变量更好

尽量减少对变量的赋值的语句,有助于提高可读性。