第七章,高质量的子程序前言子程序是为实现一个特定的目的而编写的一个可被调用的方法或过程。 7.1 创建子程序的正当的理由1、 降低复杂度。当内部循环或内部嵌套层次很深的时候,就要考虑从子程序中提取出新的子程序了。子程序以其抽象,封装,信息、细节的隐藏来实现管理复杂度的功能。 2、 引入中间、易懂的抽象。通过命名将一段代码重组织,使其更易理解。 3、 避免代码重复。 4、 支持子类化。便于子类覆盖。 5、 隐藏顺序。 6、 隐藏指针操作。指针操作可读性通常很差,且容易出错。 7、 提高可移植性。 8、 简化复杂的布尔判断。 9、 改善性能。隐藏实现的细节,有助于后面用更好的算法进行重新实现。 10、 确保子程序都很小 总之,创建高质量的子程序,有助于使用抽象、封装,信息细节隐藏的手段,来降低复杂度。 除此之外的理由: 1、 隔离复杂度。 2、 隐藏实现细节。 3、 限制变化带来的影响。 4、 隐藏全局数据:是对全局数据的范围都通过子程序来实现。避免直接对全局数据的范围。这个要注意一下的,是个非常好的建议。 5、 形成中央控制点。 6、 促进可重用的代码。 7、 达到特定的重构的目的。 编写有效子程序时,一个最大的心里障碍是不情愿为一个简单的目的而编写一个简单的子程序。事实证明,一个很好而有小巧的子程序会非常有用:既可以提高可读性,又便于扩展。
7.2 在子程序上的设计在设计类的时候,抽象和封装已经很大程度上取代了内聚性。但是在子程序这一层次上,内聚性仍是常用的启发式方法。 对与子程序而言,内聚性是指子程序中各个操作之间的联系的紧密程度。 我们的目标是让子程序只把一件事情做好,不再做任何其他事情。 内聚性是针对操作的概念。即操作具有内聚性。通常,一个操作指一个语句,或一个函数调用。一个是简单的操作,一个是复杂的操作。 内聚性的一些概念,理解概念有助于思考如何让子程序尽可能的内聚。 1、 功能的内聚性:让一个子程序仅执行一项操作。这是最强的也是最好的内聚性。其他的内聚性则不够理想。 2、 顺序上的内聚性:子程序内包含按照特定顺序执行的操作,这些步骤需要共享数据,而只有全部执行完毕后,才完成了一下完整的功能。顺序上的内聚性设计成功能上的内聚性需要对顺序进行分解,形成多个功能更加单一的子程序。 3、 通信上的内聚性:子程序中的不同操作使用了同样的数据,当不存在其他任何联系。优化的方法是将子程序分解为多个子程序。 4、 临时的内聚性:子程序中含有一些因为需要同时执行才放到一起的操作。优化方法是把临时性的子程序看成是一系列事件的组织者,即让它直接调用其他的子程序,而不是直接执行所有的操作。另外,可以通过函数的命名,表达子程序完成的功能。避免使用多个操作叠加的方法命名,因为它暗示子程序只有巧合的内聚性。 除功能的内聚性外,其他类型的内聚性都是不可取的。要想办法避免。
1、 过程上的内聚性:子程序中的操作是按特定的顺序进行的,除此外没有任何联系。对子程序分解,把不同的功能纳入不同的子程序中,让调用方的子程序具有单一而完整的功能。 2、 逻辑上的内聚性:若干操作被放入同一个子程序中,通过传入的控制标志选择执行其中的一项操作。各个操作间没有任何的逻辑关系,这些操作只是被包含在一个很大的case或if语句中。如果此子程序只是分发命令,根据命令调用不同的子程序,则这种做法也是不错的。其他的不好的情况,可以通过分解来进行优化。分解为多个独立的子程序。 3、 巧合的内聚性:子程序内部的各个操作间没有任何的关联。
内聚性考虑的是在一个子程序内部,操作的关联程度。
编写功能上的内聚性的子程序几乎总是可能的,因此把注意力集中于功能上的内聚性,从而得到最大的收获。 使用上面的建议对我的流程引擎程序进行内聚性的优化,发现确实很有价值。值得在后面的设计和编码中坚持这种优化。
如果一个子程序中,局部变量的个数超过7个,则有可能是此子程序设计出现问题,有可能要优化一下。
7.3 好的子程序名字1、 好的子程序的名字能够清晰的描述子程序所做的一切。 2、 描述子程序所做的所有事情 子程序的名字应该能够描述其所有的输出结果及副作用。 3、 避免使用无意义的,模糊或表述不清的动词——handle,perform,output,process,dealwith等。很多情况下,使用模糊不清的动词作为子程序的名称的根本原因是程序本身就不是完成一个清晰的功能,这个时候,就要考虑重新组织该程序,并且选择准确的词语描述子程序。 4、 不要仅通过数字来形成不同的子程序名称。 5、 根据需要确定子程序名称的长度。研究表名,变量名的最佳长度是9到15个字符(过短则无法表达准确其含义,过长则有碍阅读)。子程序命名的重点是含义清晰。长短要视名字是否清晰而定。 6、 给函数命名是要对返回值有所描述。最好能精确描述返回的结果。 7、 给过程起名时使用语气强烈的动词加宾语的形式。如果是面向对象,且动词是对对象的操作,则不用加宾语。 8、 准确使用仗词(成对的词语):close/open。 9、 为常用操作确立命名规则。
7.4 子程序可以有多长子程序的长度尽量控制在200行以内。在这个范围内,代码长度不是子程序的关键因素,其他的降低复杂度的因素才是关键的。
7.5 如何使用子程序的参数子程序间的接口是程序中最容易出错的部分之一。 1、 按照输入-输出-修改的顺序排列参数:仅作输入参数-即做输入又作输出-仅作输出。 2、 考虑创建自己的OUT和IN关键字:有弊端,C++中可以使用const来表达只是输入的含义。 3、 如果几个程序都用了类似的一些参数,应该让这些参数的排列顺序保持一致。 4、 使用所有的参数。如果没有使用,则从接口中删除。 5、 把状态或出错变量放在最后。 6、 不要把子程序的参数用作工作变量。 7、 在接口中对参数的假定加以说明。比注释更好的做法是加断言:1)参数是仅用于输入,输出还是修改;2)表示数量的参数的单位;3)如果没有用枚举,则要说明状态代码和错误值的含义。4)所能接收的范围。5)不应该出现的特定数值。 8、 把子程序的参数限定在大约7个以内。7是一个神奇的数值,人很难记住超过7个单位的信息。如果子程序的参数过多,则要考虑使用设计的方法进行优化。 9、 可以考虑采用某种表示输入,输出,修改的命名规则。 10、 为子程序传递用以维持其接口抽象的变量或对象。考虑这个问题的时候,角度应该是这个子程序的接口要表达何种抽象。如果传递对象能够表达子程序的抽象,就传递对象,如果传递变量能够表达抽象,就传递变量。如果传递对象,但是在调用子程序前后经常需要进行装配和卸载,或者传递变量,但是经常要修改参数表,则要考虑是否要换一种方式来设计接口。 11、 使用具名参数? 12、 确保形式参数和实际参数匹配。C语言存在这个问题,c++,java不存在。因为他们是强类型的语言。
7.6 使用函数时要特别考虑的问题在现代编程语言中,函数是指有返回值的子程序,过程是指没有返回值的子程序。 1、 什么时候使用函数,什么时候使用过程:把子程序的调用和对状态值的判断清楚的分开,降低此语句的复杂度。如果一个子程序的主要用途就是返回其名字所指明的返回值,那么就使用函数,否则,使用过程。 2、 设置函数的返回值:1)检查所有的路径,设置一个默认的函数返回值。2)不要返回指向局部的应用和指针。
7.7 宏子程序和内联子程序1、把宏表达式包含在整个括号内:#define cube(a) ((a)*(a)*(a)) 2、把多条语句的宏用括号括起来。 3、用子程序的命名方法来命名宏,便于后面替换。 4、不到万不得已,不要用宏来替换子程序。 5、限制使用内联子程序。Inline关键字可以支持提高性能。
本章要点1、 创建子程序的最主要的目的是提高程序的可管理性,当然也有其他一些好的理由。其中,节省代码空间只是一个次要的原因;提供可读性、可靠性和可修改性等原因更重要一些。 2、 有时候,把一些简单的操作写成独立的函数也非常有价值。 3、 子程序可以按照其内聚性分为很多种类,而你应该让大多数子程序具有功能上的内聚性,这是最佳的一种内聚性。 4、 子程序的名字是它的质量的指示器,如果名字糟糕但恰如其分,说明这个子程序的设计很差劲。如果名字糟糕而且又不准确,那么它就反映不出是干什么的。不管怎么样,糟糕的名字都意味着程序需要修改。 5、 只有在某个子程序的主要目的是返回由其名字所描述的特定结果时,才使用函数。 6、 细心的程序员会非常谨慎的使用宏,而且只用在万不得已时。
本章,我认为最重要的部分是7.2 在子程序上的设计,这里可以学习一下子程序内聚性的知识。应用其中的一些原则对我的代码进行优化,发现效果非常不错。 |
《代码大全学习笔记》 第七章,高质量的子程序
最新推荐文章于 2023-07-11 22:14:01 发布