高质量的子程序

创建子程序不仅仅是为了重用和更容易调试。下面给出更多合理的理由。

创建子程序的正当理由

  • 降低复杂度,隐藏细节。
  • 引入中间,易懂的抽象。 把一段代码放进一个命名恰当的子程序中,这是对代码的最好说明,而不是用注释。
  • 避免重复代码
  • 支持子类化,如果是类的方法,那么可以在必要时可以用派生类重写这个方法
  • 隐藏顺序 把事件处理的顺序隐藏起来
  • 提高可移植性, 把一切非标准的API和操作系统等底层相关的API用一个子程序封装起来,可以提高可移植性。
  • 简化复杂的布尔判断 把布尔判断放进子程序中
  • 改善性能 通过使用子程序,你可以只在一个地方优化代码。
  • 隔离复杂度
  • 限制变化所带来的影响
  • 形成中央控制点

在子程序上的设计

功能上的的内聚性,这个是最好的内聚性,这意味着这个子程序只完成一项功能。如sin, CloseFile,GetCustomerName

不理想的内聚性

  • 顺序上的内聚性 指在子程序内包含有需要按特定顺序执行的操作,这些步骤需要共享数据,而且只在全部执行完成后才完成一项特定的功能
  • 通信上的内聚性 指子程序只是共享了数据,代码之间不存在任何联系
  • 临时的内聚性 指一些需要同时执行才放到一起操作的子程序。 如 StartUp。 解决这个问题是让这个子程序的代码分成多个子程序,去调用其它的多个子程序

不可取的内聚性

  • 过程上的内聚性 指一个子程序按特定的顺序执行。 解决方式:可以把不同的操作放进各自的子程序中
  • 逻辑上的内聚性 指若干个操作放入同一个子程序,通过传入一个控制标志来执行不同的操作。 如InputAll 根据控制标志来判断输入的是姓名,不是用户ID。如果这些子程序共享数据可以考虑包裹到类中
  • 巧合的内聚性

好的子程序的名字

  • 描述子程序做的所有事情。 如果这样使得这个子程序名字很长,那么要考虑这个子程序设计的是否合理,需要重构不。
  • 避免使用模糊或表述不清的动词 如HandleCalculation, DealSomething, ProcessInput这些都不能准确说明子程序到底做了什么
  • 不要仅通过数字来分别子程序
  • 根据需要确定子程序名字的长度 最好是在9-15个字符之间
  • 给函数名字时要描述它的返回值
  • 给过程命名时要用强烈的动词加宾语的形式
  • 准确使用对仗词

    add/remove open/close new/delete begin/end insert/delete show/hide create/destroy lock/unlock get/put first/last start/stop up/down next/previous get/set old/new

  • 为常用操作确立命名规则。 比如如何获取ID

    混乱的方式 employ.id.Get() people.GetID() product.id()

子程序的长度尽量控制在200行以内

如何使用子程序的参数

  • 按照输入-修改-输出的顺序放置参数
  • 如果几个子程序使用了相同的参数,保持顺序一致
  • 使用所有的参数。 不用的参数要删除
  • 把状态或出错变量放到最后
  • 不要把子程序的输入参数当作工作变量
  • 在接口中对参数的假定加以说明。 比如度量信息,使用的是米,不是英里。 可以用注释说明,更好的方式是使用断言
  • 为子程序传递用以维持其接口抽象的变量或对象
  • 使用具名参数
  • 确保实际参数各形式参数相匹配

使用函数,还是过程

函数是指有返回值的子程序(没有输出参数,只有输入参数),过程是没有返回值的子程序(可以用输出参数) 简而言之,就是一个子程序的主要用途是返回其名字所指明的返回值,那么就用函数,否则用过程

设置函数的返回值

  • 检查所有可能的返回路径
  • 不要返回局部引用和指针

要点

  • 尽量不要使用宏子程序
  • 创建子程序主要是管理复杂度,使得程序更可靠,更易读,易修改
  • 如果子程序的名字糟糕但恰如其分,要重构代码
  • 有时候把一些简单的操作写成子程序也是非常值得去做的。
  • 只有在某个子程序主要目的是返回其名字所描述的返回值时才用函数,否则用过程

Comments