OO作业总结(1-3)
前言
这三次作业,是我第一次,真正的系统性接触和学习Java,在这三次作业中,我对Java的理解也逐渐加深,也逐渐领悟到了从C语言入门的我在应对工程代码方面的不足,这一次学习到的度量分析又为我打开了新的大门。
(一)代码的度量分析
(1)第一次作业
第一次作业,内容是多项式计算,其中并没有涉及到复杂的计算原理和程序逻辑,而是在输入和输出的处理上略有难度。对于当时不懂正则表达式的我来说,我的第一次作业使用了状态自动机。
在第一次作业中,分为3个类,Input类处理输入,Polynomial作为主类,Term类表示单个多项式。之前我一直觉得这份代码写得还算不错,直到我最近学习了Java代码的度量分析,才惊觉之前的代码有着诸多缺点。
接下来贴上分析结果截图
显然,我的循环复杂度(Cyclomatic complexity)在Input类里过高了,也就是说我的Input类功能不够单一,做不到功能内聚,需要再划分为更小的模块,才能减小程序的漏洞数。
这次由于将状态机封装进了Input类,导致的Input类复杂度过高,以后的状态机或许可以分成多个简单类的交互。
(2)第二次作业
第二次作业,内容是傻瓜电梯的调度。虽然指导书刻意复杂化了代码逻辑,但其实代码非常之简单,只需要每次处理当前请求时,将请求队列之中的同质请求消除就可以了。
在这一次作业当中,我也意识到了try,catch的重要性,在面临可能数组越界或是访问空指针的危险情况时,try and catch变得尤为重要,这样可以快速帮助锁定错误所在,希望以后我也能像各位大牛一样将try catch运用熟练。
下面贴上类图和度量分析结果
本次代码设计上的主要问题从度量分析看来主要来自于Request类的SelfCheck方法,但是本人点进去仔细查看SelfCheck函数后却并不认为其有什么问题,代码如下:
1 boolean SelfCheck(double LastTime) 2 { 3 if(Time<LastTime)return false; 4 if(Type==0 && (Aim==0 || Aim>10))return false; 5 if(Type==1 && (Dep<=1 || Dep>10))return false; 6 if(Type==2 && (Dep==0 || Dep>9))return false; 7 if(Time>(1L<<32)-1)return false; 8 return true; 9 }
这个功能仅仅用来判断Request类是否合法,而由于限制条件的众多,代码内的众多if语句也难以避免,况且该功能十分明确且简单,个人不认为这段代码有不妥的地方。
以上就是这次分析的结果,个人认为这次的情况正好对应了维基百科上所说的对于特殊情况,代码的循环复杂度可以酌情放宽。
(3)第三次作业
关于第三次作业,我需要提前申明的是,本次作业我一开始的想法就错了,导致后面回天乏术。具体原因会在第二部分的bug互查说明,这里仅仅分析代码的性能。
下面贴上度量分析结果:
下面贴上Watcher中的FindDir作为示例:
1 Request FindDir(double time) 2 { 3 if(time==L.GetLast()+1 && L.IsOpen()) 4 { 5 //System.out.println("LastTime="+L.GetLast()+" time="+time); 6 L.Change(time); 7 //System.out.println(L); 8 if(Temp.GetFor()==0) 9 { 10 if(L.GetCnt(L.GetPos())>0 && L.GetReq(L.GetPos(), 1).GetAim()==L.GetPos()) 11 { 12 OUT.Add("VALID", 0, L.GetReq(L.GetPos(), 1), L.GetPos(), time); 13 L.pop(L.GetPos()); 14 } 15 if(F[L.GetPos()].GetQn()>0 && F[L.GetPos()].GUP(1).GetDep()==L.GetPos()) 16 { 17 //System.out.print("First:");F[L.GetPos()].GUP(1).Print(); 18 OUT.Add("VALID", 0, F[L.GetPos()].GUP(1), L.GetPos(), time); 19 F[L.GetPos()].PopQ(); 20 } 21 if(F[L.GetPos()].GetDn()>0 && F[L.GetPos()].GDN(1).GetDep()==L.GetPos()) 22 { 23 OUT.Add("VALID", 0, F[L.GetPos()].GDN(1), L.GetPos(), time); 24 F[L.GetPos()].PopD(); 25 } 26 while(L.GetCnt(L.GetPos())>0 && L.GetReq(L.GetPos(), 1).GetAim()==L.GetPos()) 27 { 28 OUT.Add("#SAME", 0, L.GetReq(L.GetPos(), 1), L.GetPos(), time); 29 L.pop(L.GetPos()); 30 } 31 while(F[L.GetPos()].GetQn()>0 && F[L.GetPos()].GUP(1).GetDep()==L.GetPos()) 32 { 33 OUT.Add("#SAME", 0, F[L.GetPos()].GUP(1), L.GetPos(), time); 34 F[L.GetPos()].PopQ(); 35 } 36 while(F[L.GetPos()].GetDn()>0 && F[L.GetPos()].GDN(1).GetDep()==L.GetPos()) 37 { 38 OUT.Add("#SAME", 0, F[L.GetPos()].GDN(1), L.GetPos(), time); 39 F[L.GetPos()].PopD(); 40 } 41 } 42 } 43 //System.out.println("haha="+L.GetAim()+" "+L.GetPos()+" "+L.IsOpen()+" time="+time+" last="+L.GetLast()); 44 //L.Print(time); 45 if(L.GetAim()==L.GetPos() && !L.IsOpen()) 46 { 47 //System.out.println("hahahahahahahhahaha"); 48 //L.Print(time); 49 50 //L.Print(time); 51 int aim1=0, aim2=0, aim3=0; 52 if(time==L.GetLast()) 53 { 54 int finish = L.Finish(); 55 if(finish!=0) 56 { 57 L.Direct(L.GetAim()-L.GetPos()==0 ? 0 : L.GetAim()-L.GetPos()==1 ? 1 : -1); 58 return new Request(-1); 59 } 60 } 61 //L.Print(time); 62 for(int i=1;i<=10;++i) 63 { 64 if(L.GetCnt(i)>0 && (aim1==0 || L.GetFront(i).GetSeq()<L.GetFront(aim1).GetSeq())) 65 aim1 = i; 66 if(F[i].GetQn()>0 && (aim2==0 || F[i].GUP(1).GetSeq()<F[aim2].GUP(1).GetSeq())) 67 aim2 = i; 68 if(F[i].GetDn()>0 && (aim3==0 || F[i].GDN(1).GetSeq()<F[aim3].GUP(1).GetSeq())) 69 aim3 = i; 70 } 71 if(aim1>0 && 72 (aim2==0 || (F[aim2].GetQn()>0 && L.GetFront(aim1).GetSeq()<F[aim2].GUP(1).GetSeq())) && 73 (aim3==0 || (F[aim3].GetDn()>0 && L.GetFront(aim1).GetSeq()<F[aim3].GDN(1).GetSeq()))) return L.GetFront(aim1); 74 if(aim2>0 && 75 (aim3==0 || (F[aim3].GetDn()>0 && F[aim2].GUP(1).GetSeq()<F[aim3].GDN(1).GetSeq())) && 76 (aim1==0 || (L.GetCnt(aim1)>0 && L.GetFront(aim1).GetSeq()>F[aim2].GUP(1).GetSeq()))) return F[aim2].GUP(1); 77 if(aim3>0 && 78 (aim2==0 || (F[aim2].GetQn()>0 && F[aim2].GUP(1).GetSeq()>F[aim3].GDN(1).GetSeq())) && 79 (aim1==0 || (L.GetCnt(aim1)>0 && L.GetFront(aim1).GetSeq()>F[aim3].GDN(1).GetSeq()))) return F[aim3].GDN(1); 80 return new Request(-1); 81 } 82 //L.Print(time); 83 return new Request(); 84 }
可见由于之前的逻辑分析失误,导致了if-else语句的滥用,由此可见,在写代码之前,有一个清晰,简单的框架是写好程序之必备,而本人在这一方面则反了经验不足的错误,以为简单按照描述来写就行,结果表明不成熟的程序框架会导致大量的冗余代码和复杂的·逻辑判断,今后必须引以为戒。
(二)bug分析
(1)第一次作业
第一次作业本人存在一个bug,那就是没有判断程序最开始是空格的情况。因为本人的最初版本程序要求程序第一个左括号,所以空格会导致判断为ERROR。
由此可见两个问题:
1.对内置函数不熟练,本来可以用string.replace来解决
2.逻辑思维不够严密
(2)第二次作业
第二次作业本人由于对助教意思的误解,对于同质请求没有输出#SAME,这是一个incomplete
(3)第三次作业
而第三次作业,本人为了完全再现电梯接受请求的反应,决定按照时间的运行来模拟,而这时由于一开始得考虑不周,本人犯了一些严重的错误:
1.电梯请求放在了电梯类,楼层请求放在了楼层类,导致交互时的代码非常繁琐
2.由于一开始的考虑不周,放弃了请求队列的想法,本意是想每次接受请求时只与当前请求比较,结果发现请求有可能排成队列
3.对于时间和请求的交互请求关系考虑不周,导致出现电梯没有及时改变方向,出现runtime error
综上所述,本人的第三次作业并没有达到理想要求,并非是修补bug可以解决的问题需要推倒重来。
以后也要吸取教训,对待程序一定要思考清楚后开始写,否则仅是浪费时间而已。