第四章 两人合作
4.1 代码规范
“代码规范”可以分成两部分:
1.代码风格规范。主要是文字上的规定,看似表面文章,实际上非常重要。
2.代码设计规范。牵涉到程序设计、模块之间的关系、设计模式等方方面面的通则。
4.2 代码风格规范
代码风格的原则:简明,易读,无二义性
代码风格规范:1.缩进:最好用四个空格;
2.行宽:行宽必须限制,可以限定为100字符;
3.括号:在复杂的条件表达式中,用括号清除地表示逻辑优先级;
4.断行与空白的{}行;
5.分行:不要把多条语句放在一行上。并且不要把多个变量定义在一行上;
6.命名;
7.下划线;
8.大小写:Pascal —— 所有单词的第一个字母都大写。
Camel —— 第一个单词全部小写,随后单词随Pascal形式。
所有的类型/类/函数名都用Pascal形式,所有的变量都用Camel形式。
函数中get/set中例外。
9.注释:不要注释程序是怎么工作的(How),程序本身就应该能说明这一问题。
注释是为了解释程序做什么(What),为什么这样做(Why),以及要特别注意的地方。
复杂的注释应该放在函数头,很多函数头的注释都用来解释参数的类型等,如果程序正文已经能够说明参数的类型in/out,就不要重复。
注释要随着程序的修改而不断更新,一个误导(Misleading)注释往往比没有注释更糟糕。
注释(包括所有源代码)应该只用ASCII字符。
4.3 代码设计风范
代码设计规范不光是程序书写的格式问题,而且牵涉到程序设计、模块之间的关系、设计模式等方方面面。
代码设计风范:1.函数:现代程序设计语言中的绝大部分功能,都在程序的函数(Function、Method)中实现。关于函数最重要的原则是:只做一件事,并且要做好。
2.goto:函数最好有单一的出口,为了达到这一目的,可以使用goto。只要有助于程序逻辑的清晰体现,什么方法都可以使用,包括goto。
3.错误处理:参数处理和断言。
4.如何处理C++中的类:如何处理C++中的类:如果只是数据的封装,用struct即可、公共/保护/私有成员、数据成员、虚函数、构造函数、析构函数new 和delete、运算符、异常、类型继承。
4.4 代码复审
代码复审形式:
1.自我复审:自己vs.自己,用同伴复审的标准来要求自己。不一定最有效,因为开发者对自己总是过于自信。如果能持之以恒,则对个人有很大好处。
2.同伴复审:复审者vs.开发者,简便易行。
3.团队复审:团队vs.开发者,有比较严格的规定和流程,适用于关键代码,以及复审后不再更新的代码。覆盖率高——有很多双眼睛盯着程序,但效率可能不高(全体人员都要到会)。
代码复审的目的:1.找出代码的错误,比如:a.编码错误,比如一些碰巧骗过了编译器的错误;b.不符合团队代码规范的地方
2.发现逻辑错误,程序可以编译通过,但是代码的逻辑是错的
3.发现算法错误,比如使用的算法不够优化,边界条件没有处理好等
4.发现潜在的错误和回归性错误——当前的修改导致以前修复的缺陷又重新出现
5.发现可能需要改进的地方
6.教育(互相教育)开发人员,传授经验,让更多的成员熟悉项目各部分的代码,同时熟悉和应用领域相关的实际知识
为什么要做代码复审:人不可能不犯错误;让新工能更快的熟悉代码,快速融入到团队当中。
代码复审的步骤:1.代码必须成功地编译,在所有要求的平台上,同时要编译Debug|Retail版本。编译要用团队规定的最严格的编译警告等级(例如C/C++中的W4)
2.程序员必须测试过代码。
3.程序员必须提供新的代码,以及文件差异分析工具。
4.复审者可以选择面对面的复审、独立复审或其他方式。
5.在面对面的复审中,一般是开发者控制流程,讲述修改的前因后果。但是复审者有权在任何时候打断叙述,提出自己的意见。
6.复审者必须逐一提供反馈意见。注意,复审者有权提出很多看似吹毛求疵的问题,复审者不必亲自调查每一件事,开发者有义务给出详尽的回答。
7.开发者必须负责让所有的问题都得到满意的解释或解答,或者在TFS中创建新的工作项以确保这些问题会得到处理。
8.对于复审的结果,双方必须达成一致的意见。
(1)打回去——复审发现致命问题,这些问题在解决之前不能签入代码。
(2)有条件地同意——发现了一些小问题,在这些问题得到解决或记录之后,代码可以签入,不需要再次审核。
(3)放行——代码可以不加新的改动,签入源码控制服务器。
程序员不能两次犯同样的错误。在代码审核后,开发者应该把复审过程中的记录整理出来:
1.更正明显的错误。
2.对于无法很快更正的错误,要在项目管理软件中创建Bug把它们记录下来。
3.把所有的错误记录在自己的一个“我常犯的错误”表中,作为以后自我复审的第一步。
代码复审的核查表:
1. 概要部分
(1)代码符合需求和规格说明吗?
(2)代码设计是否考虑周全?
(3)代码可读性如何?
(4)代码容易维护么?
(5)代码的每一行都执行并检查过了吗?
2. 设计规范部分
(1)设计是否遵从已知的设计模式或项目中常用的模式?
(2)有没有硬编码或字符串/数字等存在?
(3)代码有没有依赖于某一平台,是否会影响将来的移植(如Win32到Win64)
(4)开发者新写的代码能否用已有的Library/SDK/Framework中的功能实现?在本项目中是否存在类似的功能可以调用而不用全部重新实现?
(5)有没有无用的代码可以清楚?
3. 代码规范部分
修改的部分符合代码标准和风格吗?
4. 具体代码部分
(1)有没有对错误进行处理?对于调用的外部函数,是否检查了返回值或处理了异常?
(2)参数传递有无错误,字符串的长度是字节的长度还是字符的长度,是以0开始计数还是以1开始计数?
(3)边界条件是如何处理的?switch语句的default分支是如何处理的?循环有没有可能出现死循环?
(4)有没有使用断言(Assert)来保证我们认为不变的条件真的得到满足?
(5)对资源的利用,是在哪里申请,在哪里释放的?有无可能存在资源泄露?有没有优化的空间?
(6)数据结构中有没有用不到的元素?
5. 效能
(1)代码的效能(Performance)如何?最坏的情况是怎样的?
(2)代码中,特别是循环中是否有明显可优化的部分?
(3)对于系统和网络的调用是否会超时?如何处理?
6. 可读性
代码可读性如何?有没有足够的注释?
7. 可测试性
代码是否需要更新或创建新的单元测试?
如何正确的给予反馈:
1. 最外层:行为和后果
2. 中间层:习惯和动机
3. 最内层:本质和固有属性