导航

OO第一次博客

Posted on 2018-04-02 22:56  shawnco  阅读(174)  评论(0编辑  收藏  举报

第一次作业分析

1)类图

2)OO度量

可以看到,此次作业的圈复杂度较高,这主要还是因为在完成第一次作业时没有用面向对象的思想来写代码,所有的运算过程都被分解为一个个不同的方法全部放在了polyCompute中,这就导致了polyCompute类负担了整个程序的功能,造成了圈复杂度过高。

3)作业总体分析

由于之前没有JAVA的基础,这次作业开始时遇到了一定的困难,但是通过和同学的交流还是完成了此次作业。从类图可以看出这此作业整个程序中只有一个public class,实际的思想还是面向过程,而这是不符合面向对象的设计要求的,需要在以后的设计中多加注意。

第二次作业分析

1)类图

2)OO度量

这此作业依旧是圈复杂度过高。主要是因为电梯类中的run方法承担了过多的职能,它接收到合法指令后,需要判断同质指令、修改运行状态、输出运行结果,这体现出了我在划分类不够平衡,将太多的功能整合到了run之中,需要在以后的编程中多加注意。

3)BUG相关分析

这一次作业里最主要的BUG如下:

  a:    

else if(s.getime()<=sTime) {
                    sTime+=Math.abs(floorGap)*0.5;
                    if(floorGap==0)    sMove="STILL";
                    else if(floorGap>0) sMove="UP";
                    else if(floorGap<0) sMove="DOWN";
                    if(s.getdest()!=sFloor||preType.equals("FR")) { ...

  这是此次作业中影响最大的一个BUG,因为这个BUG,导致多个测试点全部没通过。

  经过后来的分析,这个BUG的原因就是因为在判断“ER”类型的同质指令时,我想当然地认为在当前指令发出时间先与下一条指令的前提下,如果前一条指令不是是“FR”类型且请求楼层与当前楼层相同时就可以认定其为同质指令,对此条件取反即可得到非同质指令的条件,可是我却忽略了第一条有效指令是(ER,1,0)的情况。因为前一条指令的目标楼层的变量sFloor和前一条指令类型preFloor分别被初始化为sFloor=0;preType="NULL",这就导致了第一条有效指令为(ER,1,0)时直接被判定为同质指令而没有输出。

if(s.getdest()!=sFloor||!preType.equals("ER")) {  ...

  后来,我将原来的判定非同质指令条件改为上述情况时,就不会再有第一条有效指令为(ER,1,0)时直接被判定为同质指令而没有输出的情况。虽然乍一看两条指令没有太大的区别,指令除了“ER”类型不就只有“FR”类型了吗,但是需要注意我们还为preType初始化为了“NULL”,而这种情况是切不可忽略的,否则就会导致同质指令的误判。

  从此次教训中,我充分认识到了不注重细节极有可能酿成大错,在写代码时必定再三斟酌,确保自己思考时不漏掉某一种情况,同时应该尽量减少多层if-else语句嵌套的代码风格,因为这样的写法很容易漏掉某种特殊情况,而导致程序出错,同时还会给调试带来一定的困扰。此外,这一个BUG不算是很难发现的,而我在测试程序的过程中居然还没有发现,说明我构建测试样例的覆盖率还远不足够,需要多加锻炼。

  b:

  对指令发出时间的极限情况考虑不够周全。

  在原代码中,我没有考虑到输入的时间在int最大值临近区间内的情况,对十位及以内的时间输入都判定为有效,这就导致了输入时间大于int最大值时,指令本该被判定为无效指令却被作为有效指令加入到了请求队列中,这无疑是没有认真考虑边界情况的结果。此外,因为时间被我初始化为一位小数,且时间都是与一位小数以及整数进行运算,所以我对时间的输出没有加以格式要求,这就导致了程序在遇到特别大的时间输入时,将其转变为科学计数法后再输出,这明显背离了指导书对于输出的要求。

  对于这个BUG,我做了以下改正:

  首先在输入数据时对时间输入是否小于int最大值加以判断,将大于int最大值的输入过滤出去。

if(Double.parseDouble(m1.group(4))-4294967295.0>=1e-20) return false;
                else this.time=Double.parseDouble(m1.group(4));

  其次,在结果输出时我用String.format()对时间输出加以了“只输出一位小数”的限制,保证时间输出符合指导书的要求。

System.out.print(s.getdest()+","+sMove+","+String.format("%.1f", sTime));

  C:

  FR指令的误判

  第二次作业指导书中这句话重点在“一个楼层按钮”,这隐含向上和向下按钮是分开的,而我读到这句话是就断章取义地认为同一时刻同一楼层不能同时有向上和向下请求,这就导致了我直接将同一时刻的向上向下请求判定为无效请求而产生了BUG,这归根结底还是因为我在阅读指导书的时候没有静下心思考理解,急于将指导书读完得不偿失。

if(s.getflo()!=sFloor||!preDire.equals(s.getdir())) {
                        if(preType.equals("ER"))    sTime+=1.0; ...

 

  此外我在对于FR指令是否为STILL的判定也因为粗心大意而出现了BUG,我在写代码时认为之前指令只要是ER类型则当前的FR指令状态就为STILL,现在看来这个判定条件无疑是不成立且过于武断的,但是当时居然写出了这样的代码,果然还是过于急于求成了,以后应该理清逻辑再写出具体代码,切不可脑子里还没有理清脉络就慌忙下手。

事后经过思考,我将判断条件改为以下:

if(s.getflo()!=sFloor||!preDire.equals(s.getdir())){
                        if((preType.equals("ER")||!preDire.equals(s.getdir()))&&s.getflo()==sFloor)    sTime+=1.0;

  这段代码过段时间来看估计还得理一会思路才能看懂,所以以后还是应该尽量避免逻辑过于复杂的if条件,减少if-else语句的多重嵌套以达到清晰化思维逻辑的目的,为后期的调试提供便利。

4)作业总体分析

  这一次作业犯的错误主要还是在一些细节上,以充当判断条件的if-else语句中的BUG最为突出,我认为。这类BUG频频在第二次作业中·出现的原因除了思考问题的不够周全以外,多重嵌套的if-else语句造成的过于复杂的逻辑也给后期的debug带来了不小的阻碍,所以在以后的作业中应当首先充分研读作业指导书的要求,确保输出格式之类的细节问题不出差错,其次应当采用更加优良的代码风格,抽象出更加清晰的逻辑,避免使用多重的判断语句。

  这一次作业在总体的设计上还存在一些缺陷,比如request类中的很多private属性都设置了get方法和set方法,写出这些方法时我的初衷是为了方便其他方法访问以及修改请求的属性,从而为请求的处理提供便利,但是,在总结课后,我才认识到这与面向对象程序的设计是背道而驰的,一个类应该保证其独立性,如果它的属性能够被另一个类修改,就脱离了其private属性的初衷,还会给造成程序的耦合性较高,给后期的维护以及修改带来麻烦,在以后编程时应该对这个要求多加关注。

第三次作业分析

1)类图

2)OO度量

这次作业圈复杂度和嵌套块深度过高。圈复杂度过高很大原因是因为第三次作业还是沿袭了第二次作业的run方法,使单个方法兼任了过多的职能。而嵌套块深度过高的原因是因为在newschedule之中选取主指令和计算可捎带指令时使用用了for循环,且判断同质指令时又多次嵌套语句,这样的设计使得程序的逻辑不够明晰,不便于后期的维护和调试,以后写代码时有必要尽量改正这种代码风格,尝试使逻辑脉络清晰,便于理解。

3)BUG相关分析

修正了上次作业的BUG后,此次作业又出现了新的BUG。

a)在一次开关门执行多个同层楼的捎带指令时,未充分考虑输出顺序。一次开关门输出多个指令时应该按照输出的顺序输出,而我在输出时却没有考虑到这一输出原则,先输出了处于“副指令”地位的同层指令,导致了BUG的出现。这个BUG的主要原因还 是因为对指导书输出原则的规定没有准确理解造成的。

b)主指令输出的问题。在原代码中输出主指令后,时间加1就作为主指令完成时间,而这就恰恰忽略了主指令为Still状态的情况,Still时指令输出时间就是指令的完成时间,如果对其再加1,得到的指令完成时间就不再准确,这主要还是因为在设计时考虑不够完善。

c)在调度器中获取指令信息的方法混淆。在这次作业中,获取ER类型目的楼层的方法和获取FR类型目的楼层的方法由于在命名时采取了语义相近的命名方式,这就导致了在

根据我的分析,主要的原因还是设置的属性过多,在方法中处理时考虑漏了部分属性初始化的值造成的。

4)作业总体分析

这次作业沿用了第二次作业的思路,而大量的if-else语句、for语句使得逻辑变得复杂,给调试带来了一定的难度,也就使得很多错误藏在大量判断语句中难以发现的角落中,这一方面归因于测试集不够充分,另一方面更多还是因为过多的判断语句造成了程序逻辑复杂,需要一定时间才能够理清程序的思路。

发现BUG的策略

1)首先构建比较全面的测试样例,使其包含尽可能多的情况,测试程序能否正确完成输入的请求。如果出错再构建相对较细的测试样例,缩小BUG范围知道找出具体的BUG类型。

2)思考特殊的非法输入,比如极大数输入,大量指令输入,全为同质指令,输入不合法,乱序输入,超过指导书说明的边界情况以及可能造成程序crash的输入等。测试程序是否具有良好的鲁棒性。

3)阅读代码,理解代码逻辑,仔细分析是否有逻辑上的漏洞,对特殊情况的处理是否足够完备。

心得体会

1)多看ISSUE上同学提的问题,写之前确定自己理解指导书会有事半功倍的效果,否则输出格式不符合要求之类的细节问题带来的结果可能会是毁灭性的。

2)写程序之前理清逻辑思路,边写边想很容易会考虑不周全,忽略某些重要的边界条件。

3)在设计在程序时应仔细考虑自己的编程思想是否是面向对象的,不要陷入之前写面向过程程序的思维惯性中,要保证各个类的独立性,减少get、set方法的使用。

4)设计程序时应该努力降低代码的逻辑复杂度,划分类时应该注意均衡性,不应该使一个类承担过多的功能,避免使用for循环的多重嵌套和复杂的if-else语句的叠加使用。