一、关于编程规范的重要性论证
1、不支持。
1)编程规范有利于自己提高编程效率和编程质量。编码是程序员的职责,一个好的信息技术产品必然有高质量的代码,高质量的代码首先
一点它必须遵守某种编程规范。如果你的源代码被作为产品发布,那么你必须保证它和其它产品一样很好的包装并保持整洁。
2)编程规范有利于别人迅速理解自己的代码。一个软件整个生命周期内成本的80%用于维护,几乎没有一个软件在整个生命周期内全部由它的原始作者来维护。编程规范改善了软件的可读性,使工程师更加快速、彻底的理解新代码。
3)提高了团队编程的效率。 大量数据表明,软件存在问题或者隐患,很大一部分是由于未遵守基本准则所致,如果能在项目早期明确规则,则会避免许多麻烦。为了简化工作,团队中每一个编写软件的人都必须遵守编程规范。
除此之外,每个人都习惯看自己的代码,如果团队中的成员能够统一编程的规范,那么大家阅读别人代码的时候就会更加有耐心。
2、我是个艺术家,手艺人,我有自己的规范和原则。
反驳。
个人的能力是有限的,一个成功的软件必然与团队中各个成员的努力密不可分。当一个团队共同开发一个软件的时候,每个人都必须遵守共同的编程规范。否则自身个性的编程规范会对团队中的其他成员阅读代码造成障碍。
3、规范不能强求一律,应该允许很多例外。
反驳
既然是规范,就必须制定清楚、并且每个人都要遵守。比如,在同一项目组应明确规定对接口函数参数的合法性检查应由函数的调用者负责还是由接口函数本身负责。对于模块间接口函数的参数的合法性检查这一问题,往往有两个极端现象,即:要么是调用者和被调用者对参数均不作合法性检查,结果就遗漏了合法性检查这一必要的处理过程,造成问题隐患;要么就是调用者和被调用者均对参数进行合法性检查,这种情况虽不会造成问题,但产生了冗余代码,降低了效率。
《快速软件开发》中指出,一个软件的稳定性往往比其功能更重要,一旦团队成员的代码衔接出现漏洞,软件的不稳定性就会极高,从而影响到整个软件的效果。
4、我擅长制定编码规范,你们听我的就好了。
反驳。
编码规范的目的在于提高整个团队的编码效率,因此除了合理性,应当符合绝大多数成员的编码习惯。如果你使用的编码规范并不是为你的项目专门设计的,它对你的项目也许并不是最佳方案。。同样,这只是语法:非最优并不表示是不好。也许对你的项目来说它不是最理想的,但并不能表明它不值得遵守。不错,对于你的项目,你并没有从中获得该有的好处,但对于一个大型公司来说,它带来的好处是巨大的。除此之外,专门针对某个项目制定编码规范一般效果会更好。一个项目拥有自己的编码风格无可厚非。
二、代码复查
Code Review Checklist
General
- Does the code work? Does it perform its intended function, the logic is correct etc.
1)四则运算表达式生成器基本功能实现
2)在处理中文字符的输入输出上可以采用这种方式:
1 #include<locale.h> 2 locale china("chs");//use china character 3 wifstream exercise(exer);//exer为输入文件名 4 wstring str;//string -〉wtring 表示按照中文字符格式输入 5 ifstream answer(answ);//answ为输入文件名 6 string ans; 7 exercise.imbue(china);//use locale object 8 answer.imbue(china);
3)处理输入上欠考虑:当输入的表达式个数为小数时,程序无法正常遇见运行
- Is all the code easily understood?
1)代码结构清晰,分支明确
2)由于分支比较多,如果能够在每个分支的后面进行注释,能够进一步提高代码的可读性
- Does it conform to your agreed coding conventions? These will usually cover location of braces, variable and function names, line length, indentations, formatting, and comments.
1)符合运算符前后加空格、括号对齐等编程习惯
2)对一个函数进行注释时,应当对输入输出进行说明
3)注释措辞可以更加严谨一些
4)函数名、变量名规范易懂
- Is there any redundant(多余的) or duplicate(重复的) code?
1)辗转相除法求最大公因数
1 int i = 2; 2 while((i <= mole) || (i < deno)) 3 { 4 if( (mole % i == 0) && (deno % i == 0) ){ 5 mole = mole / i; 6 deno = deno / i; 7 continue; 8 } 9 i++; 10 }
=>
1 int dce(int a, int b) 2 { 3 int c; 4 if (a>b) 5 { 6 while (b != 0) 7 { 8 c = a%b; a = b; b = c; 9 } 10 return(a); 11 } 12 else 13 { 14 while (a != 0) 15 { 16 c = b%a; b = a; a = c; 17 } 18 return(b); 19 } 20 21 }
- Is the code as modular as possible?
这次项目主要的操作对象是分数,这份代码将分数类的属性及其操作独立开来,代码的模块化程度较强。但我在这里有一点建议,就是分数类尽可能独立成fraction.h和fraction.cpp,这样可以很便利的找到与分数相关的属性和操作,而不需要在source.cpp中反复翻看。
- Can any global variables be replaced?
无全局变量
- Is there any commented out code?
无。个人认为允许保留与调试有关的代码。
- Do loops have a set length and correct termination conditions?
是的。
- Can any of the code be replaced with library functions?
无
- Can any logging or debugging code be removed?
无
Security
- Are all data inputs checked (for the correct type, length, format, and range) and encoded?
1 while(r < 3){ 2 cout<<"数值过小,放过程序员吧,请重新输入"<<endl; 3 cin>>r; 4 } 5 while(r > 20){ 6 cout<<"数值过大,放过那些小学生吧,请重新输入"<<endl; 7 cin>>r; 8 } 9 cout<<"请输入将要生成的式子的个数:"<<endl; 10 cin>>n; 11 while(n > 1000){ 12 cout<<"数值过大,放过那些小学生吧,请重新输入"<<endl; 13 cin>>n; 14 }
1)考虑到输入处理还是很厉害的,不像我当时的程序只是限制了少量的输入格式,都没有对非法输入进行处理,不过考虑的不够全面
2)生成表达数个数问题可以进行动态数组内存分配或者选择vector<Math>
3)对用户可能输入的小数没有进行处理,导致程序无法识别输入并反馈错误信息
- Where third-party utilities are used, are returning errors being caught?
无
- Are invalid parameter values handled?
按理来说,不论变量的值是否有用,程序员都应当保证在程序运行的过程中任何变量的值都是经过初始化的
Documentation
- Do comments exist and describe the intent of the code?
程序配置有readme,写的比较详细
- Are all functions commented?
部分没有。
- Is any unusual behavior or edge-case handling described?
分类讨论的思路不够严谨
Testing
- Is the code testable? i.e. don’t add too many or hide dependencies, unable to initialize objects, test frameworks can use methods etc.
我对本程序的实现思路进行了梳理:
运算符的个数事实上并不属于本程序需要考虑的因素,并且在计算的过程中出现非法分数,比如负数或分母为零的情况时,只需要重新生成一个分数或 者运算符即可,如果重新生成一个表达式,程序运行的效率将会大大下降。
三、代码复查心得
1、一边运行一边读代码,可以有效提高阅读别人代码的速度
2、在审查代码的过程中,我意识到了自己程序存在的设计缺陷:
为了实现表达式的查重,我在设计中添加有两个栈——
操作数栈:记录操作数的顺序,进行加法和乘法时,总是将较小的数先进
操作符栈:记录运算符的顺序(此处有BUG)
当两个表达式的计算结果+操作数栈+运算符栈相同时,我判定两个表达式相同,然而事实上存在下述情况:
表达式 | 逆波兰式 | 操作数栈 | 运算符栈 |
1*2+1 | 1 2 * 1 + | 1 1 2 | + * |
1+1*2 | 1 2 * 1 + | 1 1 2 | * + |
2+1*1 | 1 1* 2 + | 1 1 2 | * + |
因而除了较小的操作数先进栈之外,所对应的运算符也应当插入栈中的相应位置