OO第一次博客总结
OO第一次博客总结
大一就久仰OO大名。曾经有一份真挚的JAVA课放在我的面前,我没有好好的珍惜,等到OO到来后,我才追悔莫及。人世间最痛苦的事莫过于此……现在来分析一下自己的代码和心得,总结一下一个月来的收获。
- 第一次作业
难点分析
第一次作业的要求是实现有一定鲁棒性的多项式加减法,C语言代码大家都练习过,因此算法部分其实并非难点。对于大多数刚开始接触JAVA语言和OO思想的同学来说,这次的难点主要是:JAVA语法、面向对象思想、正则表达式的使用和程序的鲁棒性。对于前两者,其实大一的python曾练习过面向对象的思想,但当要求写一个面向对象的程序时,我眉头一皱,发现事情并不简单。看了很多遍课件,与很多的人讨论,我也只是明白了什么是一个“面向对象的格式”。回看这三次的作业,都存在着某个类150+行的现象,实际上还是面向过程的。想理解怎么写才是OO的,我推荐大家还是要多看别人的代码,可能初始阶段会很艰难,比如我在第二次互测时拿到的代码有23个类……但是如果坚持看懂,一定是很有收获的。正则表达式是一个便利的工具,但也存在一定问题,先按下不表。而程序的正确性以及如何保证不crash,就需要大量的测试验证了。自己测得不够全面,就很可能被别人看到代码后,“量身定制”bug。
在设计上,我采用了一个Poly类型的数组Polylist来存放各个合法多项式,最后将数组的每一项和一个空多项式相加即可。坑在于如何判断输入时出现{(0,1),(0,1)}为非法。该输入系数均为0,若将多项式系数初始值设为0,将无法判断此输入因指数重复而非法。解决办法之一是对每个指数开一个数组,存储其是否被输入过。然而更好的办法是将系数初始值设为输入系数的上限,在最后相加时,对于系数为上限的项不予相加。
类图和度量分析
可以看到,get_Poly方法的McCabe Cyclomatic Complexity和Nested Block Depth因超出范围而标红了。McCabe Cyclomatic Complexity(圈复杂度)用来衡量一个模块的复杂程度,统计一个函数有多少个分支(if,while,for等),没有的话复杂度为一,每增加一个分支复杂度加一。圈复杂度的作者McCabe认为,圈复杂度在3~7为比较好的结构化方法,圈复杂度大说明程序代码可能质量低且难于测试和维护。Nested Block Depth(块嵌套深度)则为嵌套深度。这两块出现超标,往往说明程序设计有缺陷,或部分代码嵌套深,难以判断逻辑,或直接写成了面向过程的思想。这个get_Poly方法内容如下:
这个方法嵌套十分严重。除判断错误输入较多外,不仅多余的判断了是否有异常字符,在切分多项式时,更是用了贼蠢的遍历手动切分(手动切分也可以写的更简单吧),切分后更是直接扔掉,待存储多项式时候重新遍历。其实第一次写的时候,只用一个正则表达式匹配整个输入,比上述代码简单太多,无奈测试时发现,当输入过于复杂,正则表达式自身会引发crash,在判断为正则表达式自身的限制之后,无奈将匹配改为多次匹配。代码也因此长了许多,同时给debug产生了很大的难度,更是降低了代码的可读性,一层一层的嵌套着实令人难以读懂。但这样写也有一定的优点。频繁判断能保证错误输入时尽快结束,防止恶意输入时程序耗时过长(但实际上我判断的确实过于频繁了,增加了嵌套深度)。
测试分析
因测试较为全面,并未出现bug。而我拿到的程序中,公测和互测的错误都是如未说明输入“{}”的处理方式等Readme的错误。Readme作为程序使用的指导说明,一定要考虑到所有边界情况,否则使用者仅根据自己的理解,很可能出现偏差。而事实上,程序出现bug,大多数时候也就是在这些边界情况上。在测试时,除了少量功能性测试外,大量的精力花在了特殊情况、边界情况、超长输入上(如一条大小为1M的txt输入数据)。
- 第二次作业
难点分析
傻瓜电梯的设计与验证。傻瓜电梯的难点,应是对电梯的设计上,即如何实现电梯、分别给五个或者更多的类什么功能、各个类之间如何串联等问题上。我采用了三个double类型数组,分别存放ER、FR_UP、FR_DOWN每一层的按钮灯熄灭时间,当指令输入时间小于等于对应按钮熄灭时间则判定其同质。而电梯类elevator则存储电梯在该指令执行完成后的时间、楼层、运动状态,以供给调度器计算指令完成时间。对于指令队列,从头开始逐条取出处理。
类图和度量分析
这次标红的同样是圈复杂度和块嵌套深度,出现问题的方法除了和第一次作业类似的read模块,用于识别合法性外,还多了一个schedule方法。这个方法仅仅用于输出正确结果,但因当时考虑不周,分了指令为ER、FR_UP、FR_DOWN和电梯为运动或静止,共6种情况,过分冗余,实际上一次就可以说清。因此就不放代码了,并非难以改正的设计缺陷。但是可以看到,floor类和其他类并无连接,因为我的floor类是空的……设计上没有用到floor类,也就使得其他类的代码行数开始增加。看似不影响,实际上给第三次埋了一个坑。虽然更加OO了一点,但是从类之间的连接数量就可以发现,实际上还是不清楚究竟什么样的代码是面向过程的,从实质上来看,这次作业依然是OO壳子下的面向过程代码。
测试分析
本次作业相较上次,输入更加复杂,因此在功能性测试和边界测试、压力测试以外,加入了随机生成输入以进行测试。因为测试比较全面,同样没有bug,不过我拿到的代码不仅非常工程化,javadoc写的也非常官方,23个类真的厉害……此处不做分析,类图就不做整理了,搬运在下面了。也有同学开始时使用时间作为程序的条件,即每0.5秒取一次指令,这样大大降低了程序的运行速度,恐怕更无法通过10s的时间限制。
- 第三次作业
难点分析
ALS电梯。这次电梯的最大改进为允许捎带,这就出现了主指令和最多两条捎带指令(即若存在捎带,能找到一条最先捎带的指令,以及可能会找到一条和它仅指令标识符不同的指令一起被捎带)。因此排在后面的指令有可能被先完成,不能从队列头开始向后遍历。我使用了一个指令队列,规定头指令为主指令,判断同质后,向后找最优捎带指令,执行后从指令队列中删除。当所有比主指令先执行完的捎带指令全部完成,执行主指令并从队列删除,将新的主指令置顶。循环下去至队列为空。然而在完成代码的时候,没有思考好捎带的边界条件和更新熄灭灯的时刻,使得判断捎带和同质时存在问题。此外,要求使用的继承、接口、重写也是新的难点。
类图和度量分析
对于圈复杂度和块嵌套深度的问题,由于基本沿用了第二次作业,不做赘述。惭愧的是,虽然要求使用继承,由于第二次作业中schedule没有分到elevator和floor的部分,导致该类其实只有一个行为,在子类中还被重写了……实际并未用到继承的思想。
测试分析
这次测试水平又进步了……通过两个编译生成的.class文件和随机测试数据,可以在python程序中实现自动运行和比对结果。本次的测试重点是捎带请求和同质判断。捎带又存在功能性测试和开关门时的边界测试。而同质则要考虑到新加入的捎带请求是否为同质,又是否利用捎带请求信息更新了熄灭灯数组的时间。我自己就出现了判断捎带未注意边界条件的情况,也因此影响了灯熄灭数组的更新,使得判断同质出现了问题。此外,两次作业中schedule类明显过长,成为一个“万能”的类,是不符合面向对象思想的。也导致了这个方法过于复杂,我通宵都没能de完所有bug。
- 心得体会
关于设计和debug的体会,上文已叙。但通过读手中拿到的代码,让我意识到了dalao的代码水平是什么样的。但能读到的代码只有一份,对于大家理解面向对象思想十分有限,希望能在讲作业的过程中增加一下各种思路的介绍,能给大家带来更快的进步。