个人作业Week2-代码复审
一、代码复审Check List
1. 概要部分
代码能符合需求和规格说明么?
代码不符合需求和规格说明。测试“-c”时,生成的数独有些不合法;测试“-s”时,解题总少解一道,且文件路径有问题(Comment)。对错误参数可以处理。但对N<=1000000
并没有限制。
代码设计是否有周全的考虑?
每个子功能都用函数实现,并定义了一系列结构体
来维护数独。
代码可读性如何?
代码可读性一般。缺少必要注释和命名规范。
代码容易维护么?
不容易维护。所有600行代码都放在一个main.cpp
文件中,缺少必要注释。只有3个commit,而且代码相关都在第二个commit中。
代码的每一行都执行并检查过了吗?Comment
代码中有很多注释了的代码;我猜测是作者调试是加的,发布时又注释掉了。发布时应该删去。而且调试时临时使用的代码应该用
#ifdef DEBUG
#endif // DEBUG
包括起来。这样可以防止发布是忘记删除或分不清哪部分应该删除,而且调试时更方便。
代码覆盖测试:
-c测试
-s测试
2. 设计规范部分
设计是否遵从已知的设计模式或项目中常用的模式?
没有用到设计模式。
有没有硬编码或字符串/数字等存在?Comment
在对数独结构体格子的遍历过程中,使用了大量的硬解码数字,还有产生随机数的种子数组,也使用了硬编码。
代码有没有依赖于某一平台,是否会影响将来的移植(如Win32到Win64)?
使用C++编程,并且没有使用特定平台的API或库,所以没有依赖某一平台,不影响移植。
开发者新写的代码能否用已有的Library/SDK/Framework中的功能实现?在本项目中是否存在类似的功能可以调用而不用全部重新实现?
作者自己写了统计运行时间的代码,用到了time.h
库中的实现;对字符串的操作也用了相当多的string
的功能实现。
此项目中不存在可以调用而不用全部重新实现的功能(或许存在而我不知道,我对各种库了解的比较少)。
有没有无用的代码可以清除?(很多人想保留尽可能多的代码,因为以后可能会用上,这样导致程序文件中有很多注释掉的代码,这些代码都可以删除,因为源代码控制已经保存了原来的老代码。)
作者注释了调试过程中使用的代码,这些在发布的时候都可以清除掉。
3. 代码规范部分
修改的部分符合代码标准和风格么(详细条文略)?
代码风格遵循统一的标准和风格;不过命名规范比较欠缺,都是由小写字母组成的单词。
4. 具体代码部分
有没有对错误进行处理?对于调用的外部函数,是否检查了返回值或处理了异常?
对错误进行了处理,可以允许处理错误的参数。对外部函数的返回值进行了检查。
参数传递有无错误,字符串的长度是字节的长度还是字符(可能是单/双字节)的长度,是以0开始计数还是以1开始计数?
参数传递无错误。字符串的长度是字节的长度,因为读写处理过程中只涉及到数字的读写,是单字节字符,所以也是字符的长度。以0开始计数。
边界条件是如何处理的?Switch语句的Default是如何处理的?循环有没有可能出现死循环?
作者对参数
项目中并未出现switch语句。
不可能出现死循环。作者对循环条件和循环条件的变化处理的很好。
有没有使用断言(Assert)来保证我们认为不变的条件真的满足?
没有使用。但作者确实在头文件中include了<cassert>
,却并未使用。
对资源的利用,是在哪里申请,在哪里释放的?有没有可能导致资源泄露(内存、文件、各种GUI资源、数据库访问的连接,等等)?有没有可能优化?
存在一定的内存泄漏。在处理“-s”参数时,对象table1``new
后并未delete
(Comment),不过考虑到之后程序的进程马上会终止,程序申请的内存会被全部释放,所以影响不大。但是使用完table1
将其所申请释放,仍是一个好习惯。
数据结构中是否有无用的元素?
没有。所有定义的数据结构都被利用。
5. 效能
代码的效能(Performance)如何?最坏的情况是怎样的?
作者的解法是模拟人类求解数独的过程:先把可以确定唯一答案的空格填好,找不到这样的空格的时候,就找一个只有两个候选数的空格进行尝试。不断重复这两个过程,最终就可以把数独解出来(当然也可能无解)。生成数独时则先往固定的几个格子里随机填写数字,形成一个数独残局,再用解数独的方法进行求解,生成数独终局。效能分析图如下所示:
其中getcount函数更新每个单元(行、列、子宫格)每个数字(1~9)被占用的情况,是比较耗时的一个工作,使用3层循环实现。
由于程序本身的正确性存在问题。按照助教强调的优化顺序
make it work
make it right
make it fast
make it extensible
make it maintainable
程序无法right,fast也就无从谈起。
代码中,特别是循环中是否有明显可优化的部分(C++中反复创建类,C#中string的操作是否能用StringBuilder 来优化)?
作者虽然使用C++写的程序,但完全使用面向过程的设计方式,类都当结构体使用。一些函数可以改写成某个类的方法,可以增加可读性,而且写起来更容易。
对于系统和网络调用是否会超时?如何处理?
我们的这次个人项目并不需要网络调用。系统调用也只是文件的打开、读写、关闭操作,作者对其检查了返回值,做了正确的处理。
6. 可读性
代码可读性如何?有没有足够的注释?
代码可读性比较糟糕.主要体现在两点:
- 缺少命名规范
- 几乎没有注释; 还有有一些调试过程中加的注释, 作者并未删除
7. 可测试性
代码是否需要更新或创建新的单元测试?
作者并没有任何单元测试。需要添加对各个函数的单元测试。
还可以有针对特定领域开发(如数据库、网页、多线程等)的核查表。
我们此次个人项目不涉及任何网页、数据库、多线程(当然不排除一些大佬使用数据库和多线程),不需要任何核查表。
二、设计一个代码规范
由于我此次项目使用C#完成,所以我选择尝试StyleCop
这个代码规范工具。事实上,Visual Studio本身对C#的代码规范就比较好用。可以强制你写成MSDN
风格的代码,极大地提高可读性。
在MarketPlace
下载好StyleCop
后,我在自己的项目上Run StyleCop
,竟然报了246个Warnings。其中绝大多数是关于给文件、类、方法添加Documents和无效空格问题的。
工具提供的代码规范和你个人的代码风格有什么不同?
我个人基本上遵循MSDN
风格。一是Visual Studio本身支持的比较好;二是我经常从MSDN
上借鉴代码,看的多,复制粘贴起来也方便。
工具提供的代码规范更加严格,尤其是对文档和注释的要求极高。比如,每个文件、每个类、类的每个域、每个方法都要有文档注释说明。而且对于代码风格规范更加严格。比如一个被大括号包裹的语句块后再跟语句需要加个空行;using头文件必须在namespace里面,这个和MSDN
默认的有所不同。
工具提供的代码规范里有哪些部分是你之前没有想到的?
- using头文件必须在namespace里面,这个和
MSDN
默认的有所不同。 - 类的每个域都要有文档头,和access modifier。
为什么要这样规范?这样的规范有意义吗?
《构建之法》第4章中讲的很清楚。现代软件工程是一个群体项目,代码要写给人看,所以一个团队要有统一的代码规范。一致的代码规范有利于团队开发和维护。
我们使用的代码规范
- 使用驼峰命名
- 常量以k开头
- 用4个空格缩进
- 需要加空格的符号后面加空格
- 函数之间空一行
- 每个函数和代码块前加上详细的注释
- 有必要时在变量声明的后面加上注释
- 所有地方都不省略大括号