CodeComplete 笔记 第4部分 语句

122 阅读5分钟

Organizing Straight-Line Code 组织直线型代码

14 Organizing Straight-Line Code 组织直线型代码.png

Statements That Must Be in a Specific Order

必须有明确顺序的语句

最容易组织的连续语句是那些顺序相关的语句

如果语句之间存在依赖关系,且这些关系要求一定的顺序,那么请说法使这些关系变得明显

组织语句的简单原则

  • 说法组织代码,使依赖关系变得明显

  • 使子程序名能凸显依赖关系

  • 利用子程序参数明确显示依赖关系

    • 暗示顺序依赖关系
expenseData = InitializeExpenseData( expenseData )
expenseData = ComputeMarketingExpense( expenseData )
expenseData = ComputeSalesExpense( expenseData )
expenseData = ComputeTravelExpense( expenseData )
DisplayExpenseSummary( expenseData )
  • 用注释对不清晰的依赖关系进行说明
  • 用断言或错误处理代码来检查依赖关系

Statements Whose Order Doesn't Matter

顺序无关的语句

Making Code Read from Top to Bottom

使代码易于自上而下地阅读

Grouping Related Statements

把相关的语句组织在一起

Using Conditionals 使用条件语句

15 Using Conditionals 使用条件语句.png

if Statements

if 语句

Plain if-then Statements

简单 if-then 语句

  • 首长写正常代码路径:再处理不常见情况

  • 确保对于等量的分支是正确的

  • 把正常情况的处理放在if后面 而不是else后面

    • VB
If ( status = Status_Success) Then
    ReadFile(...)     // 正常情况
    If( status = Status_success ) Then 
        PrintSummary(...)    // 正常情况
        If( status = Status_Success ) Then
            UpdateAllAccounts(...)    // 正常情况
        Else
            errorType = ErrorType_SummarySaveError    // 出错情况
    Else
        errorType = ErrorType_FileReadError    // 出错情况
Else
    errorType = ErrorType_FileOpenError    // 出错情况
 
  • 让if 子句后面跟随一个有意义的语句
  • 考虑真的不需要else子句吗?
  • 测试else子句的正确性
  • 检查if和else子句是不是弄反了

Chains of if-then-else Statements

if-then-else 语句串

  • 利用布尔函数调用简化复杂的检测
  • 把最常见的情况放在最前面
  • 确保所有的情况都考虑到了
  • 如果你的语言支持,替换为其他结构 如 switch

case Statements

case 语句

Choosing the Most Effective Ordering of Cases

为 case 选择最有效的排列顺序

  • 按字母顺序或数字顺序排列
  • 把正常情况放在前面
  • 按执行频率排列

Tips for Using case Statements

使用 case 语句的诀窍

  • 简化每种情况对应的操作

  • 不要为了使用case而刻意制造一个变量

  • 把 default 只用于检查真正的默认情况

    • 那么增加一种新情况就很容易——你只增加这种情况和相应的代码即可
  • 利用 default 子名来检测错误

  • 在C++与JAVA里避免代码执行越过一条case子句的末尾

Controlling Loops 控制循环

16 Controlling Loops 控制循环.png

Selecting the Kind of Loop

选择循环的种类

种类

  • 计数循环 —— 执行的次数是一定的,可能是针对每位雇员执行一次
  • 连续求值循环 —— 预先并不知道多少次,它会在每次迭代时检查是否应结束
  • 无限循环 —— 一旦启动就一直执行下去
  • 迭代器循环 —— 对容器类里面的每个元素执行一次操作

When to Use a while Loop

使用时候使用 while 循环

  • 如果你预先并不知道循环要迭代多少次,那么就使用while循环
  • 检测位于循环的开始、结尾

When to Use a Loop-With-Exit Loop

什么时候用带退出的循环

  • C++ 带退出的循环,更容易维护
while ( true ) {
    ...

    if (score < targetScore) {
        break;
    }

    ...
}
- 把所有退出条件放在一处
- 用注释阐明操作意图

When to Use a for Loop

何时使用for 循环

  • 如果你需要一个执行次数固定的循环,for就是一个好选择
  • 可用for来执行那些不需要循环内部控制的简单操作
  • for的关键在于,你在循环它处把它写好后就可忘掉它了,无须在循环内部做任何事情去控制它
  • 如果存在一个必须使执行从循环中跳出的条件,那么 就应该改用 while 循环

When to Use a foreach Loop

何时使用foreach 循环

  • 很适用于对数组或其他容器的各项元素执行操作。它的优势在于此消除循环内务处理算术,从而也消除了任何由循环控制算术导致出错的可能性

Controlling the Loop

循环控制

阻止循环错误的两种方法

  • 减少能影响循环各种因素的数量!简化、简化、在简化!
  • 把循环内部当做一个子程序或一个黑盒看待——外围程序只知道它的控制条件,却不知道它的内容

Entering the Loop

进入循环

  • 只从一个位置进入循环

  • 把初始化代码紧放在循环前面

  • 用 while(true) 表示无限循环

  • 在适当情况下多使用 for 循环

    • for有助于写成可读性强的循环
  • 在while循环更适用时,不要使用for循环

Processing the Middle of the Loop

处理好循环体

  • 用 { 和 } 把循环中的语句括起来

  • 避免空循环

  • 把循环内务操作要么放在循环开始、要么放在循环末尾

    • 内务指像 i = i + 1 或 j++ 这样的表达式
  • 一个循环只做一件事

    • 循环应该和子程序一样,每个循环只做一件事并且把它做好。只在显示证明低下时再去合并它们

Exiting the Loop

退出循环

  • 设法确认循环能终止

  • 使循环终止条件看起来很明显

  • 不要为了终止循环而胡乱改动for循环的下标

    • 胡乱改动循环下标
for (int i = 0; i < 100; i++){
    // some code

    if (...){
        i = 100;
    }

    // more code
}
  • 避免出现依赖于循环下标最终取值的代码

    • 滥用循环下标的代码
for (recordCount = 0; recordCount < MAX_RECORDS; recordCount++) {
    if( entry[ recordCount ] == testValue ){
        break;
    }

    if( recordCount < MAX_RECORDS){
        return ( true )
    }
    else {
        return( false )
    }

    //
}
	- 没有滥用循环下标的代码
found = false;
for(recordCount = 0; recordCount < MAX_RECORDS; recordCount++) {
    if( entry[ recordCount ] == testValue ){
        found = true;
        break;
    }
}

return( found );
  • 考虑使用计数器

safetyCounter = 0; do { node = node->Next; ... safetyCounter++; if( safetyCounter >= SAFETY_LIMIT ){ Assert( false, "Error" ) } } while ( node->Next != NULL )


### Exiting Loops Early
提前退出循环

- 考虑在while循环中使用break而不用布尔标记

	- 可减少嵌套

- 小心那些有很多break散布其中的循环
- 在循环开始处用 continue 进行判断

	- 这样可避免一个if判断。反之,如果continue出现在中部或末尾,就应用if

- 如果语言支持,使用带标号break结构

	- ```
do{
   ...
   switch
      ...
      CALL_CENTER_DOWN:
      if(){
           ...
            break CALL_CENTER_DOWN;
           ...
      }
}
  • 使用break和continue要小心谨慎

    • 除非你已经考虑过各种替换方案,否则不要使用break
    • 如果你不能证明使用break或continue的正当性,那么就不要用它们

Checking Endpoints

检查端点

  • 对于一个简单循环,通常需要注意三种情况

    • 开始的情况
    • 任意选择的中间情况
    • 最终情况
  • 在你创建循环时,应在脑海里运行这三种循环情况,如果有一些特殊情况是与第一次或最后一次都不同,那么也要检查它们。如果循环中包含了复杂的计算,那么就拿出计算器来手动地检查这些计算是否正确

  • 是否愿意执行这种检查,是高效程序呀与低效程序员之间的一项关键差别

  • 低效程序员可能会把 < 改成 <= 。还不行,把循环下标+1 或 -1。这样可能会碰出正确的组合来,也可能把原有的错误改成了另一个更微妙的错误

Using Loop Variables

使用循环变量

  • 在嵌套循环中使用有意义的变量名来提高其可读性

    • 如果是有一个一维数组,那么用i j k还说得过去。如果是维数组,就应该用有意义的下标名
  • 把循环下标变量的作用域限制在本循环内

How Long Should a Loop Be

循环应该有多长

  • 循环要尽可能地短,以便能一目了然
  • 把嵌套限制在3层内
  • 把长循环的内容移到子程序里
  • 要让长循环格外清晰

Creating Loops Easily _ From the Inside Out

轻松创建循环——由内而外

复杂循环的简单技巧

    1. 由内而外创建循环 (伪代码) -- get rate from table -- add rate to total
    1. 转换成代码 rate = table[] totalRate = totalRate + rate
    1. 加入下标 rate = table[ census.Age ][ census.Gender ] totalRate = totalRate + rate
    1. 给现有语句外加上一层循环 For person = firstPerson to lastPerson rate = table[ census.Age ][ census.Gender ] totalRate = totalRate + rate End For
    1. 检查依赖于person循环下标的那些变量 For person = firstPerson to lastPerson rate = table[ census[ person ].Age ][ census[ person ].Gender ] totalRate = totalRate + rate End For
    1. 写出的初始化代码, 本领中, totalRate是需要初始化的 totalRate = 0; For person = firstPerson to lastPerson rate = table[ census[ person ].Age ][ census[ person ].Gender ] totalRate = totalRate + rate End For

Unsual Control Structures 不常见的控制结构

17 Unsual Control Structures 不常见的控制结构.png

Multiple Returns from a Routine

子程序中的多处返回

如果能增强可读性,那么就使用return

用防卫子句(早返回 或 早退出)来简化复杂的错误处理

减少每个子程序员中 return 的数量

Recursion

递归

对于小范围问题,使用递归会带来简单、优雅的解。但对于稍大范围里,带来的解将会极其复杂。因此要有选择使用递归

Tips for Using Recursion

使用递归的技巧

  • 确认递归能停止
  • 使用安全计算器防止出现无穷递归
  • 把递归限制在一个子程序内
  • 留心栈空间
  • 不要用递归去计算阶乘或斐波纳契数列

General Control Issues 一般控制问题

19 General Control Issues 一般控制问题.png

Boolean Expressions

布尔表达式

Using true and false for Boolean Tests

用 true 和 false 做布尔判断

  • 不要用 0 和 1

  • 隐式地比较布尔值

    • while( not done) while(a > b)

Making Complicated Expressions Simple

简化复杂的表达式

  • 拆分复杂的判断并引入梈的布尔变量
  • 把复杂的表达式做成布尔函数
  • 用决策表代替复杂的条件

Forming Boolean Expressions Positively

编写肯定形式的布尔表达式

  • 使用肯定形式

    • 令人困惑的 if( !statusOK )
    • 更清晰的 if( statusOK )
  • 用狄摩根定理简化否定

    • 否定型判断 if ( !displayOK || !printerOK )
    • 狄摩根定理 if ( !( displayOK && printerOK ) )
    • 这样就无须互换if和else中的代码

Using Parentheses to Clarify Boolean Expressions

用括号使布尔表达式更清晰

  • if( ( a < b ) == ( c == d ) )

Guidelines For Comparisons to 0

与 0 比较的指导原则

  • 隐式地比较逻辑变量

    • while( !done )
  • 把数和0相比较

    • 好的 while( balance !=0 )
    • 不好的 while( balance )

Taming Dangerously Deep Nesting

驯服危险的深层嵌套

通过重复检测条件中的某一部分来简化嵌套的if语句

把嵌套if转换为case

把深层嵌套的代码抽取出来放进单独的子程序

使用一种更面向对象的方法,如利用多态机制

重新设计深层嵌套代码

A Programming Foundation: Structured Programming

编程基础:结构化编程

即一个应用程序应只采用一些单入单出的控制结构。单入单出指的就是一个代码块,只能从一个位置开始执行,且只能结束于一个位置。

一个结构化的程序将按照一种有序且有规则的方式执行,不会做不可预知的随便跳转

The Three Components of Structured Programming

结构化编程的三个组成部分

  • Sequence 顺序
  • Selection 选择
  • Iteration 迭代(循环)