【面向对象设计与构造】第一次博客作业
一、程序结构分析
1. 第一次作业
类图
由于第一次作业难度较低,实现起来也不需要很复杂的算法,因此在编写程序的时候只建立了两个类,Main类主要负责多项式的读入以及对于输入格式的判断,Poly类则用数组对多项式进行存储和执行整个计算过程,其中Poly类的compute方法执行了整个计算过程。
度量分析
Poly类的compute方法的代码质量较低,显得比较臃肿,从度量分析图中也可以看出该方法的圈复杂度过高,容易出错。 由于之前没有接触过面向对象编程的思维和方法,所以程序的实现基本是按照面向过程的方法实现的,缺乏可扩展性。例如,如果要在多项式加减法的基础上增加对多项式乘法的支持,那么整个Poly就要推倒重来,基本没有可继承的方法和属性。
2. 第二次作业
类图
第二次作业是实现一个简单的电梯调度系统。在编程之前根据要求对五个类的功能和要实现的方法进行了规划,请求类(Request)负责判断请求是否有效,然后发出请求,请求队列(RequestQueue)通过构造一个队列来管理请求,楼层类(Floor)负责发出楼层请求,电梯类(Elevator)发出电梯内请求,记录不同时刻电梯所在楼层,调度类(Schedule)负责调度电梯运行,Main类为主程序入口,统筹全局。 由于程序结构的设计,Floor类实现的功能很少,与其他类的关联程度也较低。
度量分析
这次作业每个类的代码都比较简洁,只有在Request类中负责检查输入格式是否正确的方法比较写得复杂,代码复杂度较高,在写第三次作业时对这两个方法进行了简化并合并为一个方法。
3. 第三次作业
类图
第三次作业在第二次作业的基础上增加了捎带功能,核心部分为ALS_Scheduler类对第二次作业中的Schedule类的继承。由于调度策略的不同,子类重写了父类的work()方法,继承了检查同质请求的方法checkSameRequest(),并增加了对于可捎带请求进行检测的方法checkPiggybacking()。另外,Elevator类实现了ElevatorInterface接口中的所有方法,并重载了toString()方法,用于输出结果时调用。
度量分析
由度量分析图可以看出,Request类中实现格式检查的方法checkFormat()尽管在第二次作业的基础上进行了简化,复杂度依然较高。另外,由于增加了捎带请求,不仅要在主请求执行时每到一层就要检测是否有可捎带请求,在每一条请求执行完毕时也要输出它的所有同质请求,主请求执行完毕后还要循环判断是否有同质请求可升级为主请求,所以导致work()方法内嵌套过深,这是在今后编程中需要改进的地方。
二、程序bug分析
1. 第一次作业
在第一次作业中,由于输入的多项式格式较为复杂,如果直接利用正则表达式对输入进行匹配,在输入数据过多时就会导致溢出,因此我采用了先根据'}'对输入字符串进行分割,然后对每一部分进行正则表达式匹配,但在编写程序时忽略了多个 '}' 连续的情况,在最后的测试阶段才发现了这个问题。
2. 第二次作业
第二次作业在程序功能方面并未发现明显的bug,但是由于对于助教所补充规定的“必须输出前100条请求的结果”这一句话理解不到位,在输入超过程序指定范围且未输入“RUN”时直接结束程序,因此被互测者找到漏洞,可见我们互测时对于程序各方面的要求都非常严格,不仅要通过所有正常的功能性测试点,更要在一些细枝末节的地方下大量的功夫。
3. 第三次作业
第三次作业虽然只是在第二次作业的基础上增加了“捎带”功能,但实现起来却要比第二次作业复杂很多。对于可捎带请求的判断,在指导书中是按照请求发出时间作为基准,但这种判断方式实现时要考虑的因素较多,需要考虑多个可捎带请求同时执行等特殊情况,但如果我们转变思路,在主请求运行的整个过程中,每到一个新的楼层就判断是否有一条或多条可捎带的请求需要被执行完毕,然后只开关门一次,这样实现起来就比较简洁。但起初在编写程序时在细节方面还是存在一些问题,比如没有考虑到电梯可能在请求未执行完毕时出现空闲情况,且在到达目标楼层时,主请求和捎带请求没有严格按照输入的顺序进行输出等,在最后的自我测试阶段才发现并完善了这些问题。
三、bug分析策略
在我测试自己和他人程序的时候主要采取的策略主要有以下几点: 首先是要结合Readme文档认真读代码,分析代码的结构,在此基础上发现问题,然后对症下药,通过相应的测试样例来进行验证。这种方法虽然需要花费较多的精力,但是可以发现一些普通测试样例难以发现的问题,具有很好的针对性。在测试其他同学的作业时,发现大多数同学在程序主要功能的实现上一般不存在问题,比较容易出错的地方在于对于各种特殊输入的检测与处理,正则表达式的使用等,而这类问题是最适合通过读代码来发现的。 其次是编写自动测试脚本来对程序进行全方位的功能性测试,尤其是像第三次作业那样可能出现bug的情况较多时,这种方法就显得十分有效。 最后是再次认真读Readme文档,针对作者在文档中规定的各种特殊情况以及相应的处理方式在程序中进行一一验证,发现可能存在的问题。
四、心得体会
通过这三次作业的练习,我获得了很多收获。 首先,在面向对象编程的学习方面,每次作业虽然不需要特别复杂的算法,但对于我们面向对象编程思维和编程能力的训练确有很大帮助。通过短短几周的学习,我们已经能够用java来实现简单的面向对象程序,尽管现在写出的程序可能存在许多问题,但对于我们今后的学习来说无疑具有十分重要的奠基作用。 其次,通过每一次的互测,我看到了许多风格迥异的代码。一方面,这些代码就像一面镜子,通过读其他人的代码,我可以从中学习到许多优点和长处,同时也能在对比之下发现自身在编写代码时存在许多问题和不良习惯;另一方面,我也充分认识到拥有一个良好的代码风格的重要性,我们今后所写的代码注定要由许多其他人来阅读和维护,所以决不能写出只有自己才能读的懂的代码。 最后,我认为我们的面向对象这门课程,不仅仅是训练我们的编程能力,更重要的还是要训练我们分析各种需求的能力。尽管每一次的指导书都存在许多不够明确和完善的地方,但是一味的抱怨解决不了任何问题,如何在指导书的字里行间提炼真正的需求,才是我们每个人应该学习和提高的地方。