以下为<<代码大全2>>[第七章高质量的子程序]的摘录

编程中什么是标准,相信大家都没有办法给出一套成系统的理论,而《代码大全》的作者就是在为我们描述从设计到实现诸多大家或意识到而没有深究,又或者还没有意识到的问题,通过系统的方式为大家展开了软件开发中诸多细节。希望对大家能都所帮助!

本章探讨了以下问题:

创建子程序的正当理由

在子程序层上展开设计

起个好名字

子程序可以写多长

如何使用子程序的参数

使用函数时要特别考虑的问题

什么时候使用函数,什么时候使用过程

宏子程序和内联子程序 (内容不错)

这里的子程序指的是完成一个特定目的的方法或过程,大家通常在什么状况下决定创建子程序?这个答案可能很难清楚。但在<<编码大全>>中有完整的分析.

1.创建子程序的正当理由

 a.降低复杂度

可以通过创建子程序来隐藏一些信息,使得在实现时不必过多的考虑这些信息。当程序写好后甚至可以忘记这些细节,只要还记得如何使用。

  b.引入中间、易懂的抽象

把一段代码放入一个命名恰当的子程序中,是说明这段代码的最好方法之一。如:

if(node <> NULL) then

while (node.next <> NULL ) do

node = node.next

leafName = node.name

end while

else

leafName=""

endif

如果改成这条语就会更容易理解:

leafName = GetLeafName( node )

这个函数名GetLeafName提供更高层次的抽象,从而使代码更具可读性,也更易于理解,同时降低原来包含上面代码的子程序的复杂度。

  c.避免代码重复

如果在两段子程序内编写相似的代码,就意味着代码分解(decomposition),这就是错误。此时应当将两段子程序中的重复代码提取出来,将其中相同部分放入一个基类,然后将差异代码放入派生类中。还有另一种方法(如C语言开发),将相同的代码放入新的子程序中,然后让其余的代码来调用这个子程序。这样的代码更加可靠,因为对于验证代码的正确性,你只需要检查一处代码,同时这样也会使改动更为可靠,因为你可以避免需要做相同的修改时,却做了一些略有不同的修改。

  d.支持子类化(subclassing)

覆盖(override)简短而规整的子程序所需新代码的数量,要比覆盖冗长而混乱的子程序更少。如果你能让可覆盖的子程序保持简单,你在实现派生类的时候也会减少犯错的机会。

  e.隐藏顺序

如一个程序通常是从先从用户那里读取数据,然后再从一个文件中读取辅助数据,那么,无论是从用户那里读取数据的子程序还是从文件中读取数据的子程序,都不应该依赖另一个子程序是否已执行。一个明确的例子是先读取栈顶的数据,然后减少stackTop变量的值,这时就应当把两行代码放到一个叫PopStack()的子程序中。把这种信息隐藏起来,总比让它们在系统内到处散布要好很多。

  f. 隐藏指针操作

指针操作的可读性通常都很差,而且也容易出错。通过把这些操作隔离在子程序内部,你就可以把精力集中于操作的意图本身,而不是指针操作的细节。同时如果此类和都能在一个位置完成,那么你对代码的正确性就会更有把握。如果你发现了比指针更合适的数据类型,也可以对程序做出修改,而不用担心会破坏了那些原本要使用指针的代码。

  g.提高可移植性

可以用子程序来隔离程序中不可移植的部分,从而明确识别和隔离示来的移植工作。

 h.简化复杂的布尔判断

为了理解程序的流程,通常并没有必要去研究那些得杂的布尔判断的细节,应当把这些判断放入函数中,以提高代码的可读性,因为:(1)这样就把判断的细节放到一边了; (2)一个具有描述性的函数名字可以概括出该判断的目的。

 i. 改善性能 

通过使用子程序,你可以只在一个地方优化代码。把代码集中在一处可以更方便地查出哪些代码的运行效率低下。同时在一处优化,就能使用到(无论直接或间接)该子程序的所有代码都从中受益。

  j.确保所有的子程序都小 (NO)

如果有这么多好的理由来把代码写成子程序,这一点就没有必要了。

2.似乎过于简单而没必要写成子程序的操作

编写有效的子程序时,一个最大的心理障碍是不情愿为一个简单的目的而编写一个简单的子程序。写一个只有两三行代码的子程序可能看来有些大材小用,但经验表明,一个很好而又小巧的子程序会十分有用。

小的子程序有许多优点。其一便是它们能够提高其可读性。我曾在一个程序的十多处写了下面的代码:

points = deivceUnits * (POINTS_PER_INCH / DeviceUnitsPerInch())

多数人都能看懂这是从设备单位(device unit)到磅数(point)的转换。但是它还可以更清楚些,所以我创建了一个程序:

Function DeviceUnitsToPoints(deviceUnits:Integer):Integer

DeviceUnitsToPoints = deivceUnits *

(POINTS_PER_INCH / DeviceUnitsPerInch())

End Function

在用这个子程序取代了那些直接嵌入计算的代码(inline code)之后,程序中那十几行代码就差不多成了下面:

points = DeviceUnitsToPoints( deviceUnits)

这行代码更具可读性--甚至已经达到自我注解的地步。

这个例子还暗示出把简单操作写成函数的另一个原因:简单的操作常常会变成复杂操作。如在某些情况下,当某个设备使DeviceUnitPerInch()返回0,就意味着必须考到除零的情况,为此就需再多写3行代码。而在以子程序实现前则需要付出数十倍的工作量。

3.起个好名字

注:函数名不要用拼音,那样会有方言发音的问题。

a.描述子程序所做的所有事情

子程序的名字应当描述其所有的输出结果以及副作用(side effects).如果一个子程序的作用是计算报表总额并打开一个输出文件,那么把它命名为CompuseReportTotals就还不算完整。ComputeReportTotalAndOpenOutputFile()是很完整,但是又太长且显得有点傻。如果你写的是有一些副作用的子程序,那就会起出很多又长又傻的名字。解决的方法不是改成其它名字,而应该换一种方式编写程序,直接解决问题。

b.避免使用无意义的,模糊或表述不清的动词  

有些动词的含义非常灵活,可以涵盖几乎任何含义。像HandleCalculation(), PerformServices(0,OutputUser(),ProcessInput()和DealWithOutput()这样的子程序名字根本不能说明子程序在做什么。当然,当handle用做事件处理这一特定的技术含义时是个例外。

如果将HandleOuput()改为FormatAndPrintOutput()就更容易看清这个子程序的功能。

c.不要仅通过数字来形成不同的子程序名字    

有个程序员把所有的代码都写成一个大的函数,然后为每15行代码创建一个函数,并把它们分别命名为Part1,Part2等。这种创建子程序和给子程序命的做法实在是骇人听闻。

d.根据需要确定子程序名字的长度   

研究表明,变量名的最佳长度是9到15个字符。子程序通常比变量更为复杂,因此,好的子程序名字通常也会更长一些。另一方面,子程序名字通常是跟在对象名字后,这实际上为其免费提供了一部分名字。总之,子程序名的长短要视该名字是否清晰易懂而定。

e.给函数命名时要对返回值有所描述  

函数有返回值,因此函数的命名要应该针对其返回值进行。如比cos(),customerID.Next(),和pen.CurrentColor()都是不错的函数名,它人精确地表述了该函数将要返回的结果。

f.给过程起名时使用语气强烈的动词加宾语的形式

一个具有功能内聚性的过程通常是针对一个对象执行一种操作。过程的名字应当能反映该过程所做的事情,而一个针对某对象执行的操作就需要一个动词+宾语形式的名字,如PrintDocument() CalcMonthlyRevenues(),CheckOrderInfo()和RepaginateDocument()等。

g准确使用对仗词

命名时使用对仗词的命名规则有助于保持一致性,从而也提高可读性。像first/last这样的词组就容易理解;而像FileOpen和_lclose这样的组合就容易使人迷惑。下面列出一些常见的对仗词组:

add/remove   increment/decrement  open/close

beign/end   insert/delete     show/hide

create/destroy   lock/unlock   source/target

first/last   min/max   start/stop

get/put   next/previous   up/down

get/set   old/new

h.为常用操作确立命名规则

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

在我做过的一个项目的代码里,每个对象都被分配了一个唯一标识。我们忽视了为返回这种对象标识的子程序建立一个命名规则,以至了有了下面这些子程序名字:

employee.id.Get()

dependent.GetId()

supervisio()

candidate.id()

其中Employee类提供了其id对象,而该对象以进而提供了Get()方法;Dependent类提供了GetId方法; Supervisor类则把id和为它的默认返回值;到了项目中期,已经没人能记住哪个对象应该用哪些子程序了,但此时已经撰写了太多的代码。这样一来项目组中每个人都不得不花费不必要的精力,去记住每个对象上采用的获取id的语法细节。而这些问题完全可以通过建立获取id的命名规则而避免。

4.子程序可以写多长

注:书中首先列举历年对子程序代码长度对质量影响的研究成果!

如果要编写一段超过200行代码的子程序,那你就要小心了。对于超过200行代码的子程序来说,没有哪项研究发现它能降低成本或降低出错率,而且超过200行后,你迟早会在可读性方面遇到问题。

*关于函数复杂度,可以参考另一篇文章。

高质量的子程序的Check List:

大局事项

Y/N   创建子程序的理由充分吗?

Y/N   一个子程序中所有适于单独提出的部分是不是已经被提出到单独的子程序中了?

Y/N  过程的名字中是否使用了强烈、清晰的"动词+宾语"词组?函数的名字是否描述了其返回值?

Y/N   子程序的名字是否描述了它所做的全部事情?

Y/N   是否给常用的操作建立了命名规则?

Y/N   子程序之间是否有较松的耦合?子程序与其他子程序之间的连接是否是小的(small)、明确的(intimate)、可见的(viaible)和灵活的(flexible)?

Y/N   子程序的长度是否是由其功能和逻辑自然确定,而非是依照任何人为的编码标准?

参数传递事宜:

Y/N  整体来看,子程序的参数表是否表现出一种具有整体性且一致的接口抽象?

Y/N   子程序参数的排列顺序是否合理? 是否与类似的子程序的参数排列顺序相符?

Y/N   接口假定是否已在文档中说明?

Y/N   子程序的参数个数是否没超过7个?

Y/N   是否用到了每个参数?

Y/N   子程序是否避免了把输入参数用做工作变量?

Y/N   如果子程序是一个函数,那么它是否在所有可能的情况下都能返回一个合法的值?

[CodeComplete]创建一个函数需要理由吗相关推荐

  1. c语言标准函数库怎么建立教程,C语言入门教程-创建一个函数库

    描述 创建一个函数库 上述程序中的rand和bubble_sort函数很实用,很可能在您写其他程序时也能派上用场.为了能更方便地重复使用,您可以为它们创建一个实用工具函数库. 所有的函数库都包括两部分 ...

  2. php中创建函数的正确方法,如何在PHP中创建一个函数

    我正在尝试创建一个名为saveorder的函数.到目前为止这是我的代码: //function foo($arg_1, $arg_2, /* ..., */ $arg_n) function save ...

  3. matlab创建一个函数,Matlab创建文件、function文件和函数调用

    目录 一.建立文件 二.函数调用 二.函数句柄 三.匿名函数 想查找相关内容也可在页面按ctrl+F搜索文字 一.建立文件 新建脚本,保存后可运行,后缀为.m 二.函数调用 创建ffib.m文件,编写 ...

  4. matlab创建一个函数,matlab定义一个新函数

    本文收集整理关于matlab定义一个新函数的相关议题,使用内容导航快速到达. 内容导航: Q1:matlab怎么建立自定义函数 自定义函数的格式: function 函数名(参数) 函数表达式 end ...

  5. 【C语言】创建一个函数,利用该函数将字符串中的小写字母转换为大写字母

    原理: 这类题目主要通过ASCII(美国信息交换标准代码)码差值实现,A对应ASCII码十进制数字是65,a对应ASCII码十进制数字是97,即大小写字母之间ASCII码差值为32,想要将大写字母转换 ...

  6. 【C语言】创建一个函数,利用该函数将两个字符串连接起来

    代码: #include<stdio.h>int fun(char c[100], char d[100]){int i, j;for (i = 0; c[i] != '\0'; i++) ...

  7. 【C语言】创建一个函数,判断某一正整数是否为完数,并调用这个函数找出1000以内所有完数...

    什么是完数? 如果一个数等于它的因子之和,则称该数为"完数"(或"完全数"). 例如,6的因子为1.2.3,而 6=1+2+3,因此6是"完数&quo ...

  8. 【C语言】创建一个函数,将输入的2个数排序

    #include <stdio.h> void fun(int *x,int*y) { int t; if(*x>=*y) { t=*x;*x=*y;*y=t; } } main() ...

  9. 【C语言】创建一个函数,判断某一正整数是否为素数,并调用这个函数找出1000以内所有素数...

    #include <stdio.h> int fun(int x) { int n; for(n=2;n<=x-1;n++) if(x%n==0) break; if(n>=x ...

  10. 【C语言】创建一个函数,判断某一正整数是否为水仙花数,并调用这个函数找出1000以内所有水仙花数...

    #include <stdio.h> int fun(int x) {int a, b, c;a = x / 100;b = x % 100 / 10;c = x % 10;if (x = ...

最新文章

  1. Windows App开发之集成设置、帮助、搜索和共享
  2. ASP.NET MVC 控制器激活(二)
  3. 深层学习:心智如何超越经验2.3 解释变化
  4. 微信小程序(六) 文章详情静态页面detail
  5. maven工程中添加scala-library并非idea scala插件
  6. Gridcontrol新增行选中有关问题
  7. python 内置方法 BUILT-IN METHODS
  8. [USACO08MAR]土地征用Land Acquisition
  9. 文本分类项目GPU版本代码
  10. k8s核心技术-Helm(chart模板的使用下)---K8S_Google工作笔记0049
  11. Atitit orm优缺点 Hinaernate mybatis 区别。attilax总结
  12. DMX512标准发送及代码整理
  13. 苹果手机安兔兔压力测试软件,10款跑分软件压力测试(一)
  14. 客户端性能测试工具-Wetest、cude PC
  15. Linux磁盘空间管理利器--ncdu(为你的 系统瘦身)
  16. PDF文档翻译(英文翻译为中文)
  17. 73.【JavaWeb-01】
  18. J-LINK 软件下载地址
  19. python机械臂仿真_使用VTK与Python实现机械臂三维模型可视化
  20. 假设从键盘输入从某日凌晨零点零分零秒到现在已经经历的时间(单位:秒),编译程序计算到现在为止已经过了多少天?现在的时间是多少?

热门文章

  1. python图像识别教程pdf_如何使用Python进行PDF图片识别OCR
  2. 无人机倾斜摄影详解,三维实景建模应用
  3. QTcpServer和QTcpSocket使用详解
  4. win10远程连接win7电脑 -- 局域网实现
  5. 浅谈App Hybrid混合开发的五种方案
  6. Springboot项目调用阿里云语音服务案例【真实有用】
  7. Python网络爬虫——爬取视频网站源视频!
  8. 计算机分区硬盘有写保护,如何去掉磁盘写保护实测方法
  9. HTML期末大作业~简洁的个人博客网站前台页面HTML模板7个页面(html+css+js)
  10. 关于改进邮政编码的建议 2018-03-08