code2读书笔记-第七章 高质量子程序

首先,什么是“子程序(routine)”?
子程序是为实现一个特定的目的而编写的一个可被调用的方法(method)或过程(procedure)。

那什么又是高质量的子程序呢?回答这个问题最简单的方式就是看下边这个低质量子程序的例子:

void HandleStuff( CORP_DATA & inputRec, int crntQtr, EMP_DATA empRec,
  double & estimRevenue, double ytdRevenue, int screenX, int screenY,
  COLOR_TYPE & newColor, COLOR_TYPE & prevColor, StatusType & status,
  int expenseType )
{
int i;
for ( i = 0; i < 100; i++ ) {
  inputRec.revenue[i] = 0;
  inputRec.expense[i] = corpExpense[ crntQtr ][ i ];
  }
UpdateCorpDatabase( empRec );
estimRevenue = ytdRevenue * 4.0 / (double) crntQtr;
newColor = prevColor;
status = SUCCESS;
if ( expenseType == 1 ) {
    for ( i = 0; i < 12; i++ ) 
          profit[i] = revenue[i] - expense.type1[i];
    }
else if ( expenseType == 2 ) {
          profit[i] = revenue[i] - expense.type2[i];
        }
else if ( expenseType == 3 ) 
          profit[i] = revenue[i] - expense.type3[i];
          }

这个程序有哪些不妥呢?至少有十个不同问题。先自己列出,然后看下边这个列表:

使用子程序的好处就是因为它避免了重复的代码,从而使程序更易于开发、调试、编档和维护,等等。然而这样的解释不够好,也不够完整,下面就详细解释一下子程序。

1.创建子程序的正当理由

这里列出一些创建子程序的正当理由。有些理由互有重叠,因为本来也没有打算形成一个正交的集合。

  1. 降低复杂度 一个重要原因就是降低程序复杂度。通过创建子程序隐藏一些信息。
  2. 引入中间、易懂的抽象 把一段代码放入一个命名恰当的子程序内,是说明这段代码用意最好的方法之一。
  3. 避免代码重复 创建子程序最普遍的原因是为了避免代码重复。 如果在两段子程序内编写相似的代码,就意味着代码分解出现了差错。
  4. 支持子类化 覆盖简短而规整的子程序所需新代码的数量,要比覆盖冗长而邋遢的子程序更少。
  5. 隐藏顺序 把处理事情的顺序隐藏起来是一个好主意。 如果一个行代码执行依赖于另一行代码的执行,就应该把它们放到一个子程序中,将其执行顺序隐藏起来。
  6. 隐藏指针操作 指针操作的可读性通常都很差,而且容易出错。
  7. 提高可移植性 可以用子程序来隔离程序中不可移植的部分,从而明确识别和隔离未来的移植工作。
  8. 简化复杂的布尔判断 为了理解程序的流畅,通常没有必要去研究那些复杂的布尔判断的细节。应该把这些判断放到函数中,以提高代码的可读性,因为:(1)这样就把判断细节放到一边了;(2)一个具有描述性的函数名字可以概括出该判断的目的。
  9. 改善性能 通过子程序,你可以只在一个地方优化代码。把代码集中在一处可以更方便地查出哪些代码运行效率低下。
  10. 确保所有的子程序都很小 不是的。有时候写一个大的子程序完成还会更好。

除此之外,创建类的很多理由也是创建子程序的理由:

2.在子程序层上设计

抽象和封装,在类这一层的设计中更为适用,而内聚性,在单个子程序这一层次上,仍是设计时常用的启发方式。对子程序而言, 内聚性是指子程序中各种操作之间联系的紧密程度。

关于内聚性的讨论一般会涉及到内聚性的几个层次。理解一些概念要比记住一些特定的术语更重要。这些概念可以帮助你思考 如何让子程序尽可能的内聚。

一般来说,其它类型的内聚性都是不可取的。它们会导致代码组织混乱、难于调试、不变修改。下面就给出一些不可取的内聚性。

3.好的子程序名字

好的子程序名字能清晰地描述子程序所做的一切。这里是有效地给子程序命名的一些指导原则。

4.子程序可以写多长

在面向对象的程序中,一大部分子程序都是访问器子程序,它们都非常短小。在任何时候,复杂的算法总会导致更长的子程序。在这种情况下,可以允许子程序的长度有序地增长到100到200行(不算代码中的注释和空行)。
数十年证据表明这么长的子程序也和短小的子程序一样不易出错。与其对子程序的长度强加限制,不如关注子程序的内聚性、潜逃层次,变量数量,决策点的数量等等。

5,如何使用子程序参数

子程序之间的接口是程序中最易出错的部分之一。一项研究发现,程序中有39%的错误都是属于内部接口错误,也就是子程序间相互通信时发生的错误。以下是一些可以减少接口错误的原则。

6.使用函数时要特别考虑的问题

在很多现代语言中,都同时支持函数和过程。函数是指有返回值的子程序,过程是指没有返回值的子程序。函数与过程的区别更多是语义区别,而不是语法区别。

什么时候使用函数,什么时候使用过程

语言纯化论者们认为,一个函数应该只有一个返回值,就像数学函数一样。一种常用的编程实践是让函数像过程一样执行并返回状态值。

设置函数的返回值

使用函数总是存在返回不正确返回值的风险,当函数内有多个可能的执行路径,而其中有一个没有返回值时,这个错误就出现了。请按照以下给出的建议来做。

7.宏子程序和内联子程序


要点

目录