对于是否需要有代码规范,请考虑下列论点并反驳/支持:
1.这些规范都是官僚制度下产生的浪费大家的编程时间、影响人们开发效率, 浪费时间的东西。
2.我是个艺术家,手艺人,我有自己的规范和原则。
3.规范不能强求一律,应该允许很多例外。
4.我擅长制定编码规范,你们听我的就好了。
对于观点1,我持反对意见。
正所谓“没有规矩,不成方圆”。官僚制度在现代人看来是不好的,但是无论在那个时代,它都或多或少给社会带来一定的正面影响。与其说代码规范是官僚制度下产生的,倒不如说代码规范是IT人士为了让人快捷明了地读懂代码而自发产生的一种行为规范。
学过编译技术这门课的人都会明白,代码其实就是字符串,如果让自己编译之前的字符串时,肯定有种发疯的感觉。
例如:
int main( ) ‘\n’ { int count=read( ); ‘\n’ //if number of entries read is greater than 1 ‘\n’ //then sort( ) and compact() ‘\n’ if (count > 1) { sort( );compact( ); } ‘\n’ if (count ==0) ‘\n’ count << “no sales for this month\n” ‘\n’ else write( ); ‘\n’ return 0; ‘\n’ }
实际上是:
int main( )
{ int count=read( );
//if number of entries read is greater than 1
//then sort( ) and compact( )
if (count > 1) { sort( ); compact( ); }
if (count ==0)
count << “no sales for this month\n”;
else write( );
return 0;
}
毫无疑问,每个人都喜欢后面的表达方式。所以,有规矩我们才能看到一个有序的世界。
当然,第二种表述很多人都会这么写,的确这样写速度很快,但是我们看起来不爽。因为我们不太好看清楚每个结构体。所以有些人会做一些修改,如缩进、断行、空白{}行等等。于是代码就变成了这个样子:
int main( )
{
int count = read( );
//if number of entries read is greater than 1
//then sort( ) and compact( )
if (count > 1)
{
sort( );
compact( );
}
if (count == 0)
{
count << “no sales for this month\n”;
}
else
{
write( );
}
return 0;
}
第三种表述虽然复杂,空了不少行而且有大量缩进,导致最后写完之后花的时间较长,但是与第二种表述对比,明显能够看出总共分了几个模块,每个模块的职能。
缩进(空4个空格而不是用Tab键),距离刚刚好,而且让代码的可读性大大提高。而且在不同软件中,这种缩进都不会发生改变,移植后可读性十分高。
每行宽都不会超过100字符,我们看起来不是太累,比第一种纯字符串的表述方式更容易读懂。
每个版块之间我都空了一行,读的时候我们就会有所区分。
结构体中‘{’和‘}’我都让它们分别占用一行,而且我没有省略那些省去的括号,这让我们对每个关键字对应的内容有了明确的对应关系,当然最重要的一点就是我们在调试的时候更加方便。
对于"count == 0"这个含有运算符的表达式,我让等于号左右都空了一格;对于if后面的括号,我也让它空了一格。这些细节,直接让代码更加容易读,同时代码审查的人查看时会更舒心,工作量也会大幅度减少。
总而言之,代码写得规范的确会花费较多的时间,但是这样的工作却是一劳永逸的。不论是自己在调试的过程中,还是别人在代码阅读或者代码审查的过程中,都会相当快捷。
对于观点2,我持反对意见。
每个程序员都对编码风格有强烈的自我认同。这种感觉深植于每个人的自负中,每当和同事遇到是否应该在关键词周围使用空格时,这种讨论很容易升级而僵持不下。但是,静下来想想——这真的无所谓。不管是不是在关键词周围使用了空格,只要能达成一致,大家都能从中获得易维护和集体所有制的好处。在这种情况中,闭着眼睛,遵循一种编码风格就行了。
如果一味追求自己的风格而忽视了整体,那么你代码想要表现的知识,你所要表达的创造性就会被别人所接受。否则,一个让人不敢看的代码风格,即使再有见地,也不太可能让人接受。
对于观点3,我持保留意见。
对于一个程序猿,他可能喜欢在关键字周围加空格,但是其他程序猿可能恰好相反,如果因为这个而导致他们之间发生矛盾,那么这个规范不要会更好。
而如果程序代码量很大,测试、审查人员工作量很大,而且需要在不同平台上面运行,他们提出要求写缩进时应该空4格而不是用Tab键。那么我们这些程序猿应该遵守这种规范。
所以说我对“规范不能强求一律,应该允许很多例外”这个观点持保留意见。规范不能抓得太松,当然也不能太紧,要适当。对于每个人,我们也可以酌情给予适当的选择。于是我们的代码虽然不是完全符合标准,但是绝大部分还是会让人满意的。
对于观点4,我持反对意见。
正如观点2中我的看法一样,每个人都会有自己的代码风格,而且对自己的有强烈的自我认同。如果你说自己很擅长制定代码风格,那么你的风格设计将会有大部分参照自己的风格。而其他人会根据自己的代码习惯而对制定的代码风格不理不睬,甚至会与制定该代码风格的人产生冲突。这当然是一个团队所不希望产生的。
所以我的建议就是观点3中提到的。每个人都参与代码风格的制定,每个人都对代码风格给出自己的观点,对于一些风格赞同率较高的可以大胆采用,对于一些像关键字周围是否加空格这样争议较大的问题我们可以适当放松要求。
于是我们对于大部分代码风格问题,我们都能够得到有效的办法,而且最终每个人的意见也会很少。这对团队的影响也不会很大。
我的代码审核清单
代码审查清单
1、总体:
(1) 程序是否能工作?它是否执行了预期的功能,它的逻辑是否正确?
(2) 所有代码是否容易理解?
(3) 它符合你约定的编码习惯吗?这些通常会包括括号的位置、变量和函数命名、行宽、缩进、代码格式、注释。
(4) 有冗余的或重复的代码吗?
(5) 是尽可能模块化的代码吗?
(6) 有全局变量可以替换吗?
(7) 有任何注释掉的代码吗?
(8) 循环有一定长度或者终止条件?
(9) 有哪些代码可以被库函数替换掉?
(10)记录或者调试代码被移除了吗?
2、安全
(1) 所有的输入被检查(对类型、长度、格式、范围)和编码了吗?
(2) 哪些地方用着第三方工具?返回的错误被捕捉到了吗?
(3) 输出值被检查和编码了吗?
(4) 无效参数处理了吗?
3、文档
(1) 是否有注释和代码目的描述?
(2) 所有函数都有注释吗?
(3) 是否对不正常的行为或者边缘情况进行描述了?
(4) 第三方库的使用和功能有记录吗?
(5) 数据结构和度量单位是否解释了?
(6) 有不完整的代码吗?如果是这样,应该是删除或给一个合适的标记如“待办事项(TODO)”?
4、测试
(1) 代码可以测试吗?例如,不要加太多或者隐藏依赖关系,不能初始化对象,测试框架能用方法等。
(2) 测试存在吗?它们是否全面?最少要达到你期望的代码覆盖量。
(3) 是否在做执行目标功能的单元测试?
(4) 是否有warning?是否检测数组越界?。。。
从程序的说明文档和运行情况来看,程序可以正常运行,但是让人费解的是程序是会显示在控制台上的交互式界面,所以不太符合老师的要求:用-n、-r、-e、-a控制程序功能。
而且代码程序出现逻辑问题:不能直接根据Exercises.txt和answerfile.txt得到Grade.txt,必须先重新得到Exercises.txt和Answers.txt,然后才能得到Grade.txt。这明显有逻辑问题,我们有时候只需要题目和答案,就能得到成绩;如果每次都要重新生成,那么若你需要测试原来的文件和answerfile.txt,那么就会发生较大矛盾。
代码还是很容易理解的,不过他的程序代码不符合我的编码习惯。首先他的多个类写在了一个文件中,其次他的函数之间、类之间、小模块之间都没有空行,最后就是并不是每个函数都有注释。
程序没有太多的冗余或重复代码,非要说有的话就是优先级比较那一块。
程序的模块化很明显,没有全局变量,当然不需要全局变量的替换。程序也没有注释掉的代码,所以不会对我造成影响。
循环都有长度限制和终止条件。也没有代码可以被库函数替换掉。记录或者调试代码都被移除了。
没有对输入格式进行规范。当输入的不是整数的时候,会出现FormatException,而程序没有对其进行处理。所以输入格式检查和编码问题的没有解决。
没有使用第三方工具,而且对于系统包中一些函数,程序中也没有进行一系列的错误捕捉工作。
当不存在answerfie.txt时,若进行获取成绩这一操作,或产生FileNotFoundException,而这个异常没有得到处理。
当出现无效参数,如检验答案是输入了3(无效整数,1代表检验,2代表退出),最后不会得到任何提示,所以这个会造成一定的不友好。
程序代码中有一定的注释,这些注释十分容易让人明白代码的目的。但是,对于那些通过函数名或变量名就可以明白含义的地方就没有注释,这虽然可以理解,但是为了让读者更容易知晓编程者的意图,我觉得最好还是应该把这些地方标注出来,并且写上简单明了的注释和代码描述。
对于不正常的地方和边缘情况,程序没有进行完全处理,所以那些没有处理的地方当然没有对应的代码描述。而对于其他的不正常的地方和边缘情况,他的处理相对而言就很到位了。
程序中没有用到第三方工具,而对于其中最为明显的数据结构Fraction,他给出的名字都能很明显的读出相应的关系。但是为了明显的让给代码审核者明显看懂,我觉得更应该给一定的标注。
程序很完整,没有不完整的代码。所以不需要进行标记。
一、我的测试
1.普通测试(正常测试)
(1)题目数10,数值范围4,answerfile.txt直接是Answers.txt的拷贝。
测试:最后能够输出最后的运算表达式,但是产生的速度很慢。Grade.txt最后的答案也是正确的。但是,文件有个较大问题,就是没有进行题目、答案编号,所以看起来很麻烦。
(2)题目数10,数值范围3。
测试:自己运算这10个运算式,得到的答案与输出到Answers.txt中的答案进行比较,检测答案是否正确,经测试是正确的。然后就是看每个算式是否重复,经检测得到有相同的运算式(1 × 2 + 1、(1 × 2) + 1)。
2.测试速度,是否能生成超过10000的题,表示形式是否正确,是否出现超过3个运算符的算式
(1)题目数10000,数值范围10。
测试:看Exercises.txt, Answers.txt是否有10000项;看Exercise.txt中算式是否符合要求;看Answers.txt是否正确形式的答案。可以明显感觉到这个过程用时很长,不利于测试。所以代码需要改进。
(2)answerfile.txt直接是Answers.txt的拷贝。
测试:看Grade.txt的Correct项是否包含所有的序号,Wrong项包含的0个序号。最后测试结果与所想的完全相符。但是用时太长了。
3.测试-n的参数小于等于0,或者-r的参数小于等于0的特殊情况
(1)题目数-2,数值范围10。
输出:Exercises.txt、Answers.txt、Grade.txt都被删除,没有产生对应文件。
(2)题目数10,数值范围-2。
输出:产生ArgumentOutOfRangeException。
4.测试是否能够正确地判断答案是否正确。
(1)对于评测的输入问题,我们要求的是输入的题目和答案都是从1开始顺序编号的,不能缺也不能多;否则,请提示输入有错误。
(2)由于我们的题目都是按规范生成的,为简单起见,可以认为输入的题目都是按照顺序编号的符合规范的题目。
(3)(但是答案缺少的话请报错并给出提示。)我把这一条忽略,如果缺少的话就算它是错的。
(4)所有题号必须是按照顺序递增的,而且题数一致。
测试结果:除了第3点没问题,其他都没有实现。为了比较这个,我把总共10个算式的正确答案中的部分改错,删掉其中一个答案(题号不删除),最后得到的答案符合要求。
6.测试超小数据。
(1)题目数100,数值范围3。
输出:能产生100道题,但是最终的结果有很多重复的题。
7.测试每个算式的正确性,测试每个答案的正确性
这是一个十分繁杂的工作,在多个样例中不断查找;从同学那儿要不同的数据;自己拼凑出无法满足的数据等等。最终可以得到令人满意的答案。
二、他的测试
对于这方面,我们不太可能明显看出编程者是否进行了单元测试,但是从这份代码我可以明显感觉到这部分有些欠缺。所以我只能希望他能够在接下啊来的自我测试中用到这次的代码审核清单。
三、代码阅读测试
经过一段时间的测试,明星可以感觉到他这方面处理得很好。代码中没有任何warning,也没有出现不太可靠的地方。也没有出现数组越界的问题。
当然,其他一些方面也没有出现太多大问题,所以他的处理很好,是我需要学习的地方。