高质量的方法
1、创建方法的理由
-
降低复杂度
-
引入中间的,易懂的抽象
-
避免代码的重复
-
支持子类重写(子类重写简短而规整的方法要比冗长而邋遢的方法更好)
-
隐藏顺序
-
隐藏指针操作
-
提高可移植性
-
简化复杂的布尔判断(有时候将一些简单的操作写出独立的方法会具有更好的可读性)
-
改善性能(把代码集中在一处可以更方便地查出哪些代码的运行效率低下)
除此之外,创建类的很多理由也是创建方法的理由:
- 隔离复杂度
- 隐藏实现细节
- 限制变化所带来的影响
- 隐藏全局数据
- 形成中央控制点
- 促成可重用的代码
- 达到特定的重构目的
2、内聚性(cohesion)
内聚性(cohesion)是指方法中各种操作之间联系的紧密程度。好的方法具有高内聚性,符合单一职责原则。
内聚性的几个层次
- 功能内聚(functional cohesion)
- 它是最强也是最好的一种内聚性,也就是说让一个子程序仅执行一项操作
- 除此之外都是不够理想的内聚
- 顺序内聚(sequential cohesion)
- 模块的各个成分和同一个功能密切相关,而且一个成分的输出作为另一个成分的输入
- 比如方法需要按照给定的出生日期来计算员工的年龄和退休时间。如果该方法是先计算员工的年龄,再根据计算出的员工年龄来计算退休时间,那么该方法就是顺序内聚。
- 将该方法拆分成两个方法,A计算员工的年龄,B计算退休时间(B中调用A),则两者都是功能内聚
- 通信内聚(communicational cohesion)
- 模块中的不同操作使用或生成同一数据集
- 如上例,如果方法先计算员工的年龄,再重新计算他的退休时间,两次计算之间只是碰巧使用了相同的出生日期数据,则该方法就只具有通信内聚性
- 时间内聚(temporal cohesion)
- 模块完成的功能必须在同一时间内执行,但这些功能只是因为时间因素才有关联
- 比如在 startUp() 方法里有读取配置文件、初始化临时文件等操作
- 过程内聚(procedural cohesion)
- 模块内部的处理成分是相关的,而且这些处理必须以特定的次序进行执行
- 用户登陆了某某网站,方法A责依次读取用户的用户名、邮箱和联系方式,这个次序是事先规定的,不能改变。方法A中就是过程内聚
- 逻辑内聚(logical cohesion)
- 逻辑上相关的功能被放在同一模块中
- 方法A实现的是将对应的人员信息发送给技术部,人事部和财政部,决定发送给哪个部门由输入的控制标志决定的。方法A中就是逻辑内聚。
- 最好不要用控制标志来控制另一个方法的处理方式,应该让多个方法分别完成不同的操作
- 偶然内聚(coincidental cohesion)
- 模块的各成分之间没有关联,只是把分散的功能合并在一起
3、方法命名
方法的命名是它的质量指示器,糟糕的命名意味着程序需要修改
如何给一个方法取给好名字:
- 描述方法所做的所有事情
- 避免使用无意义的、模糊或者表述不清的动词(比如 ProcessInput())
- 不要仅通过数字来形成不同方法名字(比如 insertToDb1()、insertToDb2())
- 根据需要确定子程序名字的长度
- 给方法命名时要对返回值有所描述(比如 printer.isReady()、customerId.next()、pen.currentColor())
- 给过程起名时使用语气强烈的动词加宾语的形式(面向对象语言中不需要宾语)
- 为常用操作确定命名规则,避免相同的操作具有不同的方法名
4、方法参数
- 按照 输入->修改->输出 排列参数
- 如果几个方法都用了类似的一些参数,应该让这些参数的排列顺序保持一致
- 比如 print(int x) 使用printf(int x, File file) 就要比 printf(File file, int x) 好
- 不用的参数应该剔除
- 把状态或出错变量放在最后
- 不要把方法的参数用作工作变量
- 应该将其赋值给新的临时变量,让后续的操作去操作临时变量
- 在方法中对参数的假定加以说明(不要等把方法写完之后再回头去写注释,应该在代码中使用断言来处理)
- 方法中的参数个数不要超过7个
- 根据方法的抽象定义决定是传递需要的变量还是直接传递一个对象
- 尽量使用具名参数,使参数更加具有自我描述性
- 确保实际参数与形式参数相匹配