本单元的OO作业相比以前的,实在可以以和蔼来形容。但是和蔼并不意味着什么都不做,这单元的两次作业,特点在于每种查询难度不大,但是有很多需要商榷的细节点和查询种类比较多。由于UML图和java8之间,存在着不少的差异,所以为了厘清这些差异,也花费了不少时间,也给代码编写和测试产生了不少歧义。
第一次作业
对于类图的查询,即需要对于输入的信息,进行一定的分析和构图:输入为“类”则构建类对象,输入为“继承”则修改类对象的“父”变量,输入为“属性”则修改类对象的“属性”变量……最后对于类,形成一个类似于数的结构,而对于接口,则形成一个图的结构;图中每个节点都包含了该类(或接口)的所有相关信息,比如关联对端、方法、属性、父子等。除此之外,同时还要考虑接口的问题和继承的层次性。这次的难点在于两个地方:接口的多继承和重名对象的处理。前者我利用了dfs来遍历,后者我分析每种指令的需求,再做各自的处理。整体来说算法难度不大,但是上述细节的东西需要自己理清思路,和同学辩论充分,再下笔。
第二次作业
直接继承了上一次的作业,并在子类中新增功能而基本不动父类的代码,实现了一定的封装和分离。顺序图的构建,算比较简单的了,想法和第一次对于类图的构建相似,对每个Lifeline都建立一个对象,并将相应信息封于该对象内部。而对于状态图,其中的初始状态Pseudostate,结束态FinalState和一般态State的细节和关系困惑了我们很久,比如初始态可以有几个,初始态可以有状态转入吗,如果有多个初始态怎么处理,初始态和结束态支持像一般态那样的一些功能吗……这些问题,因为没有在指导书中明确给出,也就给了同学们很大的遐想空间,一时众说纷纭。
最后我的处理是对于初始状态Pseudostate,结束态FinalState和一般态State这三个态,各自建立一种类,他们继承自同样的一个抽象的父类。这样父类既能保证三个儿子的功能一致性,同时也能够保证自生不被实例化,产生奇怪的东西。相反,我接触到的还有另外一种写法:不建立父子关系,状态类State中,通过一个粗暴的type来区分是初始态、结束态还是一般态。我觉得这种方法虽然在这种评测下也许不会出现问题,但是总有种权宜之计,不够完美的感觉;起码,不够高级。
另外,对于对象的建立,我一度想用单例模式和工厂模式来解决,但是后来由于时间的关系,这个想法被搁置了。相比现在的程序,如果采用了单例和工厂,相比又会更加的OO。
四个单元的架构总结
第一个单元
本单元处理字符串型的多项式时,不少功能在Main直接里面实现,类与类之间的区分也不甚清晰,甚至还出现了一个类处理多种属性的字符串的情况,这也直接导致了这个功能杂糅的类中,有一个方法对于输入的处理不够充分,导致了一个重大的bug。当时对于OO的理解也比较浅显,操作上也只是按照类将一些信息的处理分开,对信息逐层解析。整体来说保留了很多面向过程的残余。我认为这单元给我最大的帮助不是关于OO的,反而是关于java语言的熟练使用。
第二个单元
本单元是多线程的理解和运用。给我印象最深的是在调多线程的bug时候的不便,尤其是本单元第三次作业,五个线程同时跑的时候,一不小心就会产生阻塞,导致结果混乱。这个单元架构中包含五个线程,分别为输入和缓冲区、控制器、电梯0、电梯1和电梯2。客户请求通过输入进入到缓冲区中,而控制器负责从缓冲区中取请求分配给空闲电梯和在特殊情况下根据电梯的请求修改缓冲区。一开始写的时候,因为没有区分好控制器的职权范围,导致了控制器和电梯功能重复于是产生了阻塞。
这次的程序采用了单例模式来处理输入模块、控制器和缓冲区,采用了观察者模式来调和控制器和电梯,类与类之间的职权也比上次更好的分开了,电梯的构建采用了工厂模式,于是可以说这次的作业相对更加的OO了。
第三个单元
本单元重点是JML的理解和使用,作为一个精确到方法实现级别的建模语言,JML对方法的限制十分的严格。当然我们发挥的空间也就比较少了,每个同学的架构估计都是相似的:通过建立一个地铁图,然后利用floyd或者dfs或者其他算法来算出距离并储存。
程序有一定的OO性。由于逻辑上的分层关系很明显,所以类的职权分的很清楚。同时我也在第三次作业中通过继承,尽可能保护了第一次和第二次作业的代码。另外,我还将核心算法floyd算法单独提出成一个类,便于调用。
第四个单元
本单元号称是UML,但是其实并不是说依靠UML图来写程序,而是做了一个UML的解析?架构如上。
四个单元的测试总结
第一单元测多项式的时候,完全靠的就是自己手造一些数据,绞尽脑汁最后也没有测出自己的bug;看到别人的自动生成数据的程序,又觉得命令行过于繁琐,畏而不前;所以bug很多。等自己终于下定决心去写自动测评的时候,这个单元都快结束了。
第二单元电梯的时候,测试的时候,完全就是自己手造,结果就是只测试了低楼层而忽略了高楼层,强测炸的香。讲道理第二单元的请求相对简单重复,便于写出随机数据生成。但是由于是多线程,所以最后的输出结果并不稳定,这也就阻断了我的思路,不知道自动测试从何下手。
第三单元JML的时候,引入了JUnit,以方法为单元进行测试,最后的结果也还不错。其实也有很多其他同学使用了JML自带的逻辑测试,但是由于未知原因,在我的环境下运行的效果并不好。另外我还写了一个随机数据生成器,与同学们互相比对结果,最后的效果也还不错。
第四单元UML的时候,自动生成数据的方式又不管用了。这个单元我也基于UML编写了不少测试,但是测试只基于手动测试。不过bug的减少,也许说明了我自己对于各种情况的考虑愈加充分和有条理,程序的鲁棒性也越来越好。
课程收获
首先OO课让我接触到Java这门语言。之前我也听说过Java是一门“什么都能做的”的语言,但是往往写的东西代码量很大,因而也没有去学习过。这个学期我与Java摸爬滚打同甘共苦了一个学期,对于他的使用也逐渐熟悉。
其次,OO课让我对面向对象的编程思想更加熟悉。从一开始的面向过程思想到各种抽象类的使用,类与类之间的功能更加区分,从忽略接口到使用接口来限制类的功能,从使用type来区分不同的但相似的对象到使用多个子类来设计对象;面向对象的设计相比面向过程的设计,灵活性更好,封装性也更好。我在实际的使用中,确确实实感受到了他的优势所在。
第三,OO课让我切实的体会到了面对一个具体的需求,如何去了解它的说明书,再写出自己的代码。每次作业老师助教发布的指导书,包含了所有的信息,我们需要去解读指导书,明确需求,分析架构,再关注细节,才能理解这次作业的实际要求。在以后的工作中,也许会遇到类似的需求书,OO教会我们的经验就很重要了。
最后,OO课让我主动的去学习各种本地测试的方法。从手造数据到JUnit测试,再到自动化大量数据的测试,各有各的优点,而在实际工作中,我也三者结合考虑来用。手造数据适合捕捉脑子中的一闪而过的可能有问题的地方,现场编写;JUnit适合对每个方法或者类进行局部的功能性测试,这个测试我一般在写完一个类之后马上进行,保证不会把明显的bug遗留到后面;自动化测试,适合全部写完之后,对整体功能进行批量的测试。三者各有优缺点,但是有一点是肯定的,充分测试再发布程序。
三个改进建议
一,我觉得在刚刚接触OO的时候,课上任务可以给一个面向对象的标程,然后要求使用其中的一些技巧比如封装、接口、抽象类、多态、包等,结合题意仿写出一个程序。相比于直接要求写一个要求众多的程序,这样的难度更低,而且更重要的是能让大家更快入门OO。
二,指导书要改了,而且是UML和JML两个单元,指导书其中含有很多歧义。
三,根据类的复杂度和依赖程度来给予一定的评分。依赖程度越低,单个类复杂度越低,结构分越高。同时结构分采用相对分数,按照排名赋分。
感谢OO。