一、metrics简介
(1)使用
· 使用metrics:首先选中需要进行度量分析的project,右键选择最下的Properties菜单,点击Metrics,选中Enable Metrics复选框,Apply,OK。
· 从 Eclipse 中选择 Window 菜单,然后选择 Show View --> Other--> Metrics View。
(2)分析
a) metrics工具量度参数(纵向):
number of overridden methods |
重载方法的数量 |
number of attributes |
属性的数量 |
number of children |
子类的数量 |
number of classes |
类的数量 |
method lines of code |
代码的方法行 |
number of methods |
方法的数量 |
nested block depth |
块嵌套深度 |
depth of inheritance tree |
继承树的深度 |
number of packages |
包的数量 |
afferent coupling |
传入耦合 |
number of Interfaces |
接口的数量 |
mccabe cyclomatic complexity |
McCabe圈复杂度 |
total lines of code |
代码的总行数 |
instability |
不稳定 |
number of parameters |
参数的数量 |
lack of cohesion of methods |
方法缺乏凝聚力 |
efferent coupling |
传出耦合 |
number of static methods |
静态方法数 |
normalized distance |
正常距离 |
abstractness |
抽象性 |
specialization index |
专业化指数 |
weighted methods per class |
每类的加权方法 |
number of static attributes |
静态属性数 |
b) 横向:各参量依次为,总数、平均数、标准差(Standard Deviation)、最大值,以及出现最大值的地方。
二、作业分析
1. 第五次作业——多线程ALS电梯
(1)metrics度量分析:
从图中看出,McCabe圈复杂度过大。它反映了控制流程图的区域数,圈复杂度大的程序代码一般质量低,且难于测试和维护。圈复杂度VG=e-n+2,其中,e表示控制流图中边的数量,n表示控制流图中节点的数量。
其中圈复杂度最大的方法是调度器中判断捎带的lookBring方法,VG过大的原因是判定条件(if-else式语句)过多。
VG标准差过大,反映出方法职责不均衡,除lookBring和调度器的run方法外,其他方法条件判断数普遍较低,应规划出更合理的职责分配办法来改进。
NBD(Nested Block Depth)较高,说明块嵌套深度较大。块嵌套深度是代码控制块(for、if、while、switch)之间互相包含的深度,较大时应做进一步的功能分解,否则于代码理解和机器执行都有一些弊端。本次作业较大NBD主要出现在调度器和输入控制模拟器的run方法中,好的风格是将功能分解放入其他函数,再在run中执行这些函数。
(2)类图:
基本设计思路是把请求队列当作托盘(共享对象),读入线程ReqSimulator为生产者,调度器MScheduler则是消费者,负责扫描请求队列并将请求分配给三部电梯;三部电梯独立执行各自请求。
(3)时序图:
2 . 第六次作业——IFTTT
(1)Metrics度量:
从metrics分析结果来看,此次作业的VG仍旧有些超出范围,需要注意分析功能模块,减少流程图区域数。
(2)类图分析:
基本设计思路是分设四个不同的触发器(均继承自父类Trigger),保存任务对象(在snapshot队列),定期扫描任务对象检测是否达到触发条件,并执行相关task,包括recover,将内容写入summary.txt或写入detail.txt文件。
3. 第七次作业——出租车呼叫模拟系统
(1) Metrics度量:
由分析结果可知,主要问题依旧是VG和NBD,不过VG的主要问题(圈复杂度较大的函数)发生在提供的GUI文件的draw方法中。而虽然guiInfo中NBD也较大,主要问题仍然在taxi类的run方法中,由于判断出租车状态使用的if-else语句是拉高NBD的主要原因。
(2)类图
基本设计思路是请求队列作为共享对象,ReadInput读入请求,Taxi处理请求。每个请求产生会打开一个请求线程,3s后结束,来模拟抢单窗口。请求内部有存放可用的出租车的队列,抢单结束后从中选择合适的出租车。出租车则负责执行请求,无请求时扫描请求队列。请求队列自身定时更新。
三、 多线程协同和同步控制分析
完成多线程ALS电梯作业时是第一次运用多线程的思想来写代码,对于线程状态转换和调度机制理解比较浅,终止线程时也尝试了很多办法,最后采用的方法是为每个线程设立一个boolean型的stop变量,记录该线程是否应该被停止。主要精力放在功能实现方面,设计层次比较糟糕,各种共享对象传来传去,线程安全性不高。
完成IFTTT时对线程并发的wait-notify机制理解更深,设计方面将作业模型抽象化为生产者-消费者模型,构建出类,尽可能减少共享对象在线程间的传递,学会了snapshot保存信息的思想。
写出租车模拟呼叫系统时,在设计原则方面,根据显式表达原则,采用枚举类型记录出租车状态和出租车应行驶的方向,使用ordinal方法获取其对应的int值。迎合SOLID原则,将方法划分的更加细致,减少代码冗余并遵循单一职责原则。对应懂我原则,变量名尽量摒弃无意义的abcxyijk,而换用有意义的字符串。在线程同步方面,采用共享对象和synchronized同步方法块,完成线程间同步。
四、 bug分析
(1)自己程序的bug
①第一次:捎带条件分析错误,某些情况不能正确判断。
②第二次:深层目录搜索失败。
③第三次:输出时间错误:将出租车服务起始时间设置为接单时间,忽略了出租车当时可能正出于waiting的200ms的状况,逻辑错误。
(2)别人程序bug
①第一次:输出格式问题。
②第二次:对文件夹监控有问题。
③第三次:输出信息问题、无法处理同时输入的多条请求,另外由于线程安全方面设计不周出现了NullPointerException的crash错误。
五、心得体会
(1)wait-notify和synchronized同步方法块是解决线程同步问题的好对策。
(2)学会使用@override注解。
(3)Enum对象的ordinal()方法有时十分方便。
(4)遵循设计原则写出来的代码质量一定更高,接下来的练习中要尽量向其靠拢。