软件工程第三次作业
博客信息 | 沈阳航空航天大学计算机学院2020软件工程作业 |
作业要求 | https://edu.cnblogs.com/campus/sau/Computer1701-1705/homework/10616 |
课程目标 | 熟悉一个“高质量”软件的开发过程 |
作业目标 | 熟悉代码规范及结对互审 |
对同伴的代码进行复审:
代码链接:<https://github.com/1013470674/-/blob/master/333>
审查表如下:
重要性 |
序号 |
结论 |
检查项 |
重要 |
1 |
是 |
命名规则是否与所采用的规范保持一致? |
2 |
是 |
是否遵循了最小长度最多信息原则? |
|
重要 |
3 |
无 |
has/can/is前缀的函数是否返回布尔型? |
注释 |
|||
重要 |
4 |
否 |
注释是否较清晰且必要? |
重要 |
5 |
否 |
复杂的分支流程是否已经被注释? |
6 |
否 |
距离较远的}是否已经被注释? |
|
7 |
否 |
非通用变量是否全部被注释? |
|
重要 |
8 |
否 |
函数是否已经有文档注释?(功能、输入、返回及其他可选) |
10 |
否 |
每行是否只声明了一个变量?(特别是那些可能出错的类型) |
|
重要 |
11 |
否 |
变量是否已经在定义的同时初始化? |
重要 |
12 |
是 |
类属性是否都执行了初始化? |
13 |
是 |
代码段落是否被合适地以空行分隔? |
|
14
|
是 |
是否合理地使用了空格使程序更清晰? |
|
15 |
是 |
代码行长度是否在要求之内? |
|
16 |
是 |
折行是否恰当? |
|
语句/功能分布/规模 |
|||
17 |
是 |
包含复合语句的{}是否成对出现并符合规范? |
|
18 |
是 |
是否给单个的循环、条件语句也加了{}? |
|
19 |
是 |
if/if-else/if-else if-else/do-while/switch-case语句的格式是否符合规范? |
|
20 |
是 |
单个变量是否只做单个用途? |
|
重要 |
21 |
是 |
单行是否只有单个功能?(不要使用;进行多行合并) |
重要 |
22 |
否 |
单个函数是否执行了单个功能并与其命名相符? |
23 |
是 |
操作符++和— —操作符的应用是否复合规范? |
规模 |
|||
重要 |
24 |
无 |
单个函数不超过规定行数? |
重要 |
25 |
无 |
缩进层数是否不超过规定? |
重要 |
26 |
否 |
是否已经消除了所有警告? |
重要 |
27 |
否 |
常数变量是否声明为final? |
重要 |
28 |
否 |
对象使用前是否进行了检查? |
重要 |
29 |
否 |
局部对象变量使用后是否被复位为NULL? |
重要 |
30 |
无 |
对数组的访问是否是安全的?(合法的index取值为[0, MAX_SIZE-1])。 |
重要 |
31 |
是 |
是否确认没有同名变量局部重复定义问题? |
32 |
否 |
程序中是否只使用了简单的表达式? |
|
重要 |
33 |
是 |
是否已经用()使操作符优先级明确化? |
重要 |
34 |
2无 |
所有判断是否都使用了(常量==变量)的形式? |
35 |
无 |
是否消除了流程悬挂? |
|
重要 |
36 |
否 |
是否每个if-else if-else语句都有最后一个else以确保处理了全集? |
重要 |
37 |
无 |
是否每个switch-case语句都有最后一个default以确保处理了全集? |
38 |
是 |
for循环是否都使用了包含下限不包含上限的形式?(k=0; k<MAX) |
|
重要 |
39 |
无 |
XML标记书写是否完整,字符串的拼写是否正确? |
40 |
无 |
对于流操作代码的异常捕获是否有finally操作以关闭流对象? |
|
41 |
否 |
退出代码段时是否对临时对象做了释放处理? |
|
重要 |
42 |
无 |
对浮点数值的相等判断是否是恰当的?(严禁使用==直接判断) |
可靠性(函数) |
|||
重要 |
43 |
否 |
入口对象是否都被进行了判断不为空? |
重要 |
44 |
否 |
入口数据的合法范围是否都被进行了判断?(尤其是数组) |
重要 |
45 |
是 |
是否对有异常抛出的方法都执行了try...catch保护? |
重要 |
46 |
否 |
是否函数的所有分支都有返回值? |
重要 |
47 |
无 |
int的返回值是否合理?(负值为失败,非负值成功) |
48 |
无 |
对于反复进行了int返回值判断是否定义了函数来处理? |
|
49 |
是 |
关键代码是否做了捕获异常处理? |
|
重要 |
50 |
无 |
是否确保函数返回CORBA对象的任何一个属性都不能为null? |
重要 |
51 |
否 |
是否对方法返回值对象做了null检查,该返回值定义时是否被初始化? |
重要 |
52 |
无 |
是否对同步对象的遍历访问做了代码同步? |
重要 |
53 |
无 |
是否确认在对Map对象使用迭代遍历过程中没有做增减元素操作? |
重要 |
54 |
是 |
线程处理函数循环内部是否有异常捕获处理,防止线程抛出异常而退出? |
55 |
否 |
原子操作代码异常中断,使用的相关外部变量是否恢复先前状态? |
|
重要 |
56 |
是 |
函数对错误的处理是恰当的? |
可维护性 |
|||
重要 |
57 |
否 |
实现代码中是否消除了直接常量?(用于计数起点的简单常数例外) |
58 |
是 |
是否消除了导致结构模糊的连续赋值?(如a= (b=d+c )) |
|
59 |
否 |
是否每个return前都要有日志记录? |
|
60 |
否 |
是否有冗余判断语句?(如:if (b) return true; else return false;) |
|
61 |
无 |
是否把方法中的重复代码抽象成私有函数? |
对代码的总结:
Java生产者消费者是最基础的线程同步问题,java岗面试中还是很容易遇到的,之前没写过多线程的代码,面试中被问到很尬啊,面完回来恶补下。在网上查到大概有5种生产者消费者的写法,分别如下。
- 用synchronized对存储加锁,然后用object原生的wait() 和 notify()做同步。
- 用concurrent.locks.Lock,然后用condition的await() 和signal()做同步。
- 直接使用concurrent.BlockingQueue。
- 使用PipedInputStream/PipedOutputStream。
- 使用信号量semaphore。
我的理解,生产者消费者模式,其实只要保证在存储端同一时刻只有一个线程读或写就不会有问题,然后再去考虑线程同步。方法1 2 5都比较类似,都是加锁来限制同一时刻只能有一个读或写。而方法3 4其实是在存储内部去保证读和写的唯一的,最低层肯定还是通过锁机制来实现的,java底层代码都封装好了而已。
我以前也写过用Java实现生产者消费者问题的代码,也曾写在了博客中,但没有专门用信号量这种方式,实际上原理和信号量相同,只是没有把控制同步的相关变量专门抽象为一个类,而且在信号量类中使用同步块封装了PV原子操作,表示他们无法被打断,这样就极大的简化了线程同步的操作。因为这样PV操作就可以清晰的在程序中表现出来,而我原先写的那个程序使用整个一个大的同步块把消费者进程和生产者进程包起来,这样虽然可以实现相关的操作,但是一个消费者生产时不能被其他线程打断,因为使用了同步块,使得程序效率下降,而消费者线程同样也是在运行时无法并行执行。而且类似PV操作的代码混杂于线程代码之中,当需要多组PV操作实现同步时,不仅编写困难,而且容易出错,出了错又难以找到位置,非常麻烦,确实给我当时的调试造成了巨大的困难。同时,使用一个类封装了需要用到的信号量和常量,并用static关键字修饰,而在类中的量本身就是静态的,这样就保证了常量可以方便的被程序调用。而之前我的程序中常量声明在了主函数中,显得不够清晰。
这是我以前写的类似问题的代码,给人感觉就比较不清晰:
public class helloworld { private final String LOCK="lock"; private final int max=10; int full=0; int mutex=0; int count=0; int copycount=1; class User extends Thread { int i; User(int i) { this.i=i; } @Override public void run() { user(); } public void user() { while(true) { synchronized(LOCK) { while(full==max) { try { LOCK.wait(); } catch(Exception e) { e.printStackTrace(); } } try { if(mutex==1) LOCK.wait(); if(count==200) break; mutex++; Thread.sleep(100); } catch(Exception e) { e.printStackTrace(); } count++; System.out.println("用户"+i+"登录"+count); mutex--; LOCK.notify(); full++; LOCK.notify(); } } } } class Copy extends Thread { @Override public void run() { copy(); } public void copy() { while(true) { if(copycount==201) { break; } synchronized(LOCK) { while(full==0) { try { LOCK.wait(); } catch(Exception e) { e.printStackTrace(); } } System.out.println("已备份"+copycount); full--; copycount++; LOCK.notify(); } } } } public static void main(String arg[]) { helloworld helloworld=new helloworld(); User u1=helloworld.new User(1); User u2=helloworld.new User(2); User u3=helloworld.new User(3); User u4=helloworld.new User(4); Copy c=helloworld.new Copy(); u1.start(); u2.start(); u3.start(); u4.start(); c.start(); } }
上述是本程序的一些优点。而本程序相对来说不是很复杂,结构相对较简单,所以没有出现明显的重复代码段,也没有必要提取为一个函数。当然程序的结构设计的还是比较清晰合理的,如果像我之前写的那样,虽然程序实现起来并不复杂,但人看起来就比较没有头绪。在声明变量时,由于变量不多,功能也相对清晰,所以把消费者和生产者的数量简短声明在一行也是合理的。虽然没有很详细的注释,但每个功能块开头都有明确的注释,功能块内部也相对清晰,实现也不复杂,所以注释还是比较合理的,过多则显得比较累赘。
程序有一个警告的原因则是在上传代码时没有把最后一个括号复制全,不算是一个程序本身的问题。wait和notify使用try,catch语句包裹起来,其他的地方如简单的输入输出也没有必要用try ,catch语句。
总体上来说此程序设计的比较合理,清晰,让人一目了然,也让我学习了相关的写法,而没有过多的程序可维护性和健壮性的保护语句,考虑到程序相对简单,也是合理的。