天天看点

高质量的子程序1

创建子程序最主要的目的是提高程序的可管理性,其次,节省代码空间也只是一个次要原因:提高可读性,可靠性和可修改性等原因都更重要一些。

我们先来看一个低质量的子程序的例子:

void HandleStuffle( 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[1];
      }
else if ( expenseType == 2){
           profit[i] = revenue[i] - expense.type2[1];
         }
else if ( expenseType == 3){
           profit[i] = revenue[i] - expense.type3[1];
        }
}
           

以下列出该段代码存在的问题:

  1. 子程序有个差劲的名字。让人一点儿也看不出这个子程序究竟是做什么的;
  2. 没有说明文档;
  3. 布局不好,过于随意;
  4. 这个子程序的输入变量 inputRec 的值被改变了。如果它是一个输入变量,她的值就不应该被修改(而且在C++中它应该被定义为 const)。如果变量的值就是要被修改的,那就不要把它命名为 inputRec;
  5. 这个子程序读写了全局变量,它从 corpExpense 中读取数值,写入 profit 。它应该更直接地与其他子程序通信,而不是去读写全局变量。
  6. 没有单一的目的。它初始化了一些变量,向数据库写入数据,又做了一些计算。从这些事情之间看不出任何联系。子程序应该有单一而明确的目的。
  7. 没有注意防范错误数据。如果 crntQtr = 0,那么表达式 ytdRevenue * 4.0 / (double) crntQtr 将会导致除零错误。
  8. 使用了若干神秘数值。100,  4.0,  12,  2,  3 等。
  9. 未使用其中一些参数:screenX 和 screenY 都没有被引用过。
  10. 一个参数传递方式有误:prevColor 被标为引用参数,单在这个字程序内却未对其赋值。
  11. 参数太多。合理的参数个数,其上限大概是 7 个左右,而这里有 11 个。而且参数的排布方式也难以理解。
  12. 参数顺序混乱且未经注释。

创建子程序的正当理由:

  1. 降低复杂度。
  2. 引入中间,易懂的抽象。把一段代码放入一个命名恰当的子程序内,是说明这段代码用意最好的方法之一。被良好命名的子程序提供了更高层次的抽象,从而使代码更具可读性,也更容易理解,同事也降低了原来包含代码的程序的复杂度。
  3. 避免代码重复。这是创建子程序最普遍的原因。
  4. 支持子类化。覆盖(override)简短而规整的子程序所需新代码的数量,要比覆盖冗长而邋遢的子程序更少。
  5. 隐藏顺序。把处理事件的顺序隐藏起来是个好主意,比让它们在系统内到处散步要好很多。
  6. 隐藏指针操作。
  7. 提高可移植性。
  8. 简化复杂的布尔判断。
  9. 改善性能。

内聚性:

对于子程序来说,内聚性是指子程序中各种操作之间联系的紧密程度。像 Cosine() (余弦函数)这样 的函数就是极端内聚的,因为整个程序只完成一项功能。而 consineAndTan()(余弦与正切)这个函数的内聚性相对较弱,因为它完成了多于一项的操作。

关于内聚性的几个层次:

  1. 功能的内聚性。是最强也是最好 的一种内聚性,也就是说让一个子程序仅执行一项操作。
  2. 顺序上的内聚性。指子程序内包包含有需要按的定顺序执行的操作,这些步骤需要共享数据,而且只有在全部执行完毕后才完成了一项完整的功能。
  3. 通信上的内聚性。指一个子程序的不同操作使用了同样的数据,但不存在其他任何联系。
  4. 临时的内聚性。含有一些因为需要同时执行才放到一起的操作的子程序。典型的例子有:Startup(), Shutdown() 等。

继续阅读