子程序是为实现一个特定的目的而编写的一个可被调用的方法(method)或过程(procedure)。
创建子程序的正当理由
这里列出一些创建子程序的正当理由:
- 降低复杂度
- 引入中间、易懂的抽象
- 避免代码重复
- 支持子类化
- 隐藏顺序
- 隐藏指针操作
- 提高可移植性
- 简化复杂的布尔判断
- 改善性能
除此之外,创建类的很多理由也是创建子程序的理由
- 隔离复杂度
- 隐藏实现细节
- 限制变化所带来的影响
- 隐藏全局数据
- 形成中央控制点
- 促成可重用的代码
- 达到特定的重构目的
在子程序上设计
关于内聚性的讨论一般会涉及到内聚性的几个层次。理解一些概念比记住一些特定的术语更加重要。
- 功能的内聚性是最强也是最好的一种内聚性,也就是说让一个子程序仅执行一项操作。
- 顺序上的内聚性是指在子程序内包含有需要按特定顺序执行的操作,这些步骤需要共享数据,而且在全部执行完毕后才能完成一项完整的功能。
- 通信上的内聚性是指一个子程序种的不同操作使用了同样的数据,但不存在其他任何联系。
- 临时的内聚性是指含有一些因为需要同时执行才放到一起操作的子程序。
- 过程上的内聚性是指一个子程序种的操作是按照特定顺序进行的。
- 逻辑上的内聚性是指若干操作被放入同一子程序中,通过传入的控制标志选择执行其中的一项操作。
- 巧合的内聚性是指子程序中的各个操作之间没有任何可以看到的联系。
好的子程序名称
好的程序名字能清晰地描述子程序所做的一切。这里是有效地给子程序命名的一些指导原则。
- 描述子程序所做的所有事情;
- 避免使用无意义的、迷糊或表述不清的动词;
- 不要仅通过数字来形成不同子程序名字;
- 根据需要确定子程序名字的长度(最佳长度是9~5个字符);
- 要对返回值有所描述;
- 给过程起名时使用语气强烈的动词加宾语的形式;
- 准确使用对仗词;
- 为常用操作确立命名规则。
子程序可以写多长
理论上认为子程序的最佳最大长度通常时一屏代码或打印出来一到两页的代码,也就是约50~150行代码。现代的计算机程序通常都是由很多极短的子程序外加少量较长的子程序组成。
如何使用子程序参数
以下是一些可以减少接口错误的指导原则。
- 按照输入-修改-输出的顺序排列参数;
- 考虑自己创建in和out关键字;
- 如果几个子程序都用了类似的一些参数,应该让这些参数的排列顺序保持一致;
- 使用所有的参数;
- 把状态或出错变量放到最后;
- 不要把子程序的参数用作工作变量;
- 在接口中对参数的假定加以说明;
- 把子程序的参数个数限制在大约7个以内;
- 考虑对参数采用某种表示输入、修改、输出的命名规则;
- 为子程序传递用以维持其接口抽象的变量或对象;
- 使用具名参数;
- 确保实际参数与形式参数相匹配。
使用函数时要特别考虑的问题
函数是指有返回值的子程序;过程是指没有返回值的子程序。
什么时候使用函数,什么时候使用过程
如果一个子程序的主要用途及时返回其名字所指明的返回值,那么就应该使用函数,否则就应该使用过程。
设置函数的返回值
使用返回值总存在返回不正确的返回值的风险。为减少这一风险,请按照下面给出的建议来做。
- 检查所有可能的返回路径;
- 不要返回指向局部数据的引用或指针。
宏子程序和内联子程序
用预处理器的宏语言编写子程序还需要一些特别的考虑。
- 把宏表达式整个包含在括号内;
- y用给子程序命名的方法来给展开后代码代码形同子程序的宏命名,以便在需要的时候可以用子程序来替换宏。
宏子程序在使用上的限制
几乎每个宏都表明在编程语言、程序或程序员身上存在问题,当你使用宏的时候,就不要指望调试器、交叉引用工具和剖测器等工具能好好工作。除非万不得已,否则不要用宏来代替子程序。
内联子程序
内联子程序避免了子程序的调用开销,可以产生非常高效的代码。
但是内联子程序要求在每个调用子程序的地方都生成该子程序的全部代码,这样无论内联子程序是长是短,都会增加整体代码的长度。
核对表:高质量的子程序
大局事项
- [ ] 创建子程序的理由充分吗?
- [ ] 一个子程序中所有适于单独提出的部分是不是已经被提出到单独的子程序中了?
- [ ] 过程的名字中是否使用了强烈、清晰的“动词+宾语”词组?函数的名字是否描述其返回值?
- [ ] 子程序的名字是否描述了它所做的全部事情?
- [ ] 是否给常用的操作建立了命名规则?
- [ ] 子程序是否具有强烈的功能上的内聚性?
- [ ] 子程序之间是否有较松的耦合?
- [ ] 子程序的长度是否由其功能和逻辑自然决定,而非遵循任何人为的编码标准?
参数传递事宜
- [ ] 整体来看,子程序的参数表是否表现出一种具有整体性且一致的接口抽象?
- [ ] 子程序参数的排列顺序是否合理?是否与类似的子程序的参数排列顺序相符?
- [ ] 接口假定是否已在文档中说明?
- [ ] 子程序的参数个数是否没超过7个?
- [ ] 是否用到了每一个输入参数?
- [ ] 是否用到了每一个输出参数?
- [ ] 子程序是否避免了把输入参数用作工作变量?
- [ ] 如果子程序是一个函数,那么它是否在所有可能的情况下都能返回一个合法的值?
要点
- 创建子程序最主要的目的是提高程序的可管理性,其次是提高可读性、可靠性、可修改性,以及节省代码空间;
- 把一些简单的操作写成独立的子程序也非常由价值;
- 子程序可以按照其内聚性分为很多类,而你应该让大多数子程序具有功能上的内聚性,这是最佳的一种内聚性。
- 子程序的名字是他的质量指示器。
- 只有在某个子程序的主要目的是返回其名字所描述的特定结果时,才应该使用函数。
- 细心的程序员会非常谨慎地使用宏,而且只在万不得已时才用。