第七章--高质量的子程序

来源:互联网 发布:php分销源码 编辑:程序博客网 时间:2024/05/17 07:27

第七章--高质量的子程序

低质量的子程序举例:

(1)命名差劲,不能反映语义。

(2)无文档

(3)程序布局风格不好

(4)输入变量被改变

(5)读写了全局数据

(6)程序功能不单一,复杂。

(7)没有注意防范错误数据。如没有除零检查等。

(8)使用了神秘数据,如100,1,2这种让人摸不着头脑的数据。

(9)传入的参数没有被使用

(10)参数传递方式错误

(11)参数太多,最多不超过7个

(12)参数混乱,且未加注释。


7.1--创建子程序的正当理由

(1)降低复杂度:通过创建子程序来隐藏信息。如,当循环或条件判断嵌套层次很深时,考虑提取成新的子程序。

(2)引入中间、易懂的抽象:将一段代码放入到一个命名恰当的子程序内,是说明这段代码的最好方式之一。

(3)避免代码重复

(4)支持子类化:如果你能让可覆盖的子程序保持简单,那么你在实现派生类的时候也会减少犯错的几率。

(5)隐藏顺序:把两行必须执行的代码顺序隐藏在一个子程序中,比将他们分布在系统四处要好很多。

(6)隐藏指针操作:指针操作的可读性差,易出错。通过隔离指针操作,就可以将精力集中在操作意图本身。

(7)提高可移植性:可以用子程序来隔离程序中不可移植的部分,从而明确识别和隔离未来的移植工作。

(8)简化复杂的布尔判断:一方面可以把判断细节放在一边,另一方面函数名可以概括判断的目的。

(9)改善性能:你可以只在一个地方优化代码,全程序收益。

(10)将多处用到的简单操作抽象成子程序,可以提高可读性。而且简单的操作往往可能变成复杂操作。


7.2--在子程序层上设计

对于子程序而言,内聚性是指子程序中各个操作间的关联紧密程度。

我们的目标是让每一个子程序只把一件事做好,不再做任何其他事情。

以下是内聚性相关的一些概念:

(1)功能的内聚性:是最强也是最好的一种内聚性,即一个子程序只执行一项操作。

(2)顺序上的内聚性:不够理想的内聚。子程序内包含有需要按特定顺序执行的操作,这些步骤共享数据,而且只有在全部执行完毕后才完成了一项完整的功能。

(3)通信上的内聚性:不够理想的内聚。指一个子程序中得不同操作使用了同样的数据但不存在其他任何联系。

(4)临时的内聚性:不够理想的内聚。指有一些因为需要同时执行才放到一起的操作的子程序。如startup(); shutdown();等。

        改善方案:将临时子程序看做是一系列事件的组织者,而非直接执行者。

下面是一个不可取的内聚性:

(5)过程上的内聚性:指一个子程序中的操作时按特定的顺序进行的。

        改善方案:将不同的操作纳入到各自的子程序中。

(6)逻辑上的内聚性:指若干个操作被放入同一个子程序中,通过传入的控制标志选择执行其中的一项操作。

                                  事件处理器是改善这种内聚性的一个参考。

(7)巧合的内聚性:是指子程序中的各个操作之间没有任何可以看到的关联。


7.3--好的子程序名字

好的子程序名字能够清晰地描述子程序所做的一切。

下面是一些命名指导原则:

(1)描述子程序所做的所有事情。

(2)避免使用无意义的、模糊或表述不清的动词。

(3)不要仅通过数字来形成不同的子程序名字。

(4)根据需要确定子程序名字的长度。

        变量名的最佳长度是9到15个字符。

(5)给函数命名时要对返回值有所描述

(6)给过程起名时使用语气强烈的动词加宾语的形式。

        在面向对象语言中,对象本身已经包含在调用语句中了,所以不要再名字中添加对象名。

(7)准确使用对仗词。

        如show/hide; open/close; start/stop;等。

(8)为常用操作确立命名规则。

        在某些系统里,区分不同类别的操作非常重要,而命名规则往往是指示这种区别的最好方式。


7.4--子程序可以写多长

与其对子程序的长度加以限定,不如让内聚性、嵌套的层次、变量的数量、解释子程序用意所需的注释数量以及其他复杂度相关的因素来决定子程序的长度。

但是,如果子程序长度超过200行,则迟早会遇到可读性方面的问题。要注意!


7.5--如何使用子程序参数

子程序之间的接口是程序中最容易出错的部分之一。以下是一些可以减少接口错误的指导原则。

(1)按照输入-修改(即是输入又是输出)-输出的顺序排列参数。

(2)考虑自己创建in和out关键字。如#define IN  和#define OUT。在C++中使用const关键字来定义输入参数通常更为适宜。

(3)如果几个子程序都用了类似的一些参数,应该让这些参数的排列顺序保持一致。

(4)使用所有的参数。

(5)把状态或出错变量放在最后。

(6)不要把子程序的参数用做工作变量。

        应该使用局部变量,把输入参数复制给局部变量。在C++中,可以使用const关键字来限制。

(7)在接口中对参数的假定加以说明。如果你假定了某个参数具有某种特征,你可以通过注释的方式加以说明。更好的一种方法是通过断言assert来进行判断。

(8)把子程序的参数个数限制在大约7个以内。

        超过了七个,请考虑重构此子程序。

(9)考虑对参数 采用某种表示输入、修改、输出的命名规则。

(10)为子程序传递用以维持其接口抽象的变量或对象。

        如何把对象的数据成员传给子程序?一种是只传递子程序所需的几个数据成员,另一种则是传入整个对象。如何选择主要取决于子程序的接口要表达何种抽象。如果只期待某几个数据,则用第一种;如果是想拥有某一个对象,则用第二种。

        例如,如果你发现要先创建一个对象,再填入子程序要得数据,再在子程序中获取这些数据。那么就说明你只是要这几个数据,而非整个对象。

(11)使用具名参数。即将形式参数和实际参数对应起来,对于减少参数列表中有多个同种类型的情况而出错是很有帮助的。

(12)确保实际参数与形式参数相匹配。

        请养成好 的习惯,总要检查参数表中参数的类型,同时留意编译器给出的关于参数类型不匹配的警告。!!!

        

7.6--是用函数时要特别考虑的问题

1.什么时候使用函数,什么时候使用过程。

    函数是指有返回值的子程序。

    过程是指没有返回值的子程序。

简而言之,如果一个子程序的主要用途就是返回其名字所指明的返回值,那么用函数,否则就应该使用过程(过程可以通过输出参数来输出东西)。


2. 设置函数的返回值

(1)检查所有可能的返回路径。考虑所有分支。

(2)不要返回指向局部数据的引用或指针。因为一旦子程序执行结束,局部数据被释放,任何指向局部数据的引用或指针也随之失效。


7.7--宏子程序和内联子程序

1. C++使用宏的一些常见情形:

(1)把宏表达式整个包含在括号内。如#define Cube(a) ((a) * (a)*(a))

(2)把含有多条语句的宏用大括号括起来。

        通常认为,用宏来代替函数的做法具有风险,而且也不易理解,因此,除非必要,否则应该避免使用这种技术。

(3)用给子程序命名的方法来给展开后形同子程序的宏命名,以便在需要的时候可以用子程序来替换宏。

        比如你用宏定义了一个“函数” p(x) ((x)*(x)) , 在代码中用了p(x++); 你的本意是想先执行完p(x), 再x++; 但是用宏之后就变成了(x++)*(x++)。所以要特别注意++ --等。

2. 宏子程序在使用上的限制

C++中又好多取代宏的方案:

(1)const 可以用于定义常量。

(2)inline可以用于定义可被编译为内嵌的代码的函数。

(3)template可以用于以类型安全的方式定义各种标准操作,如min,max等。

(4)enum可以用于定义美枚举类型。

(5)typedef可以用于定义简单的类型替换


3. 内联子程序

编译器在编译期间会把inline子程序转换为插入内嵌的代码,以避免子程序调用的开销。

     但要注意节制使用inline子程序。因为 inline子程序违反了封装的原则,因为C++要求程序员把inline子程序的实现写在头文件里。

另一方面,嵌入之后,增加了代码的长度。

    为性能原因而使用inline子程序的底线是:剖测代码并衡量性能上的改进。

0 0
原创粉丝点击