本次作业是第四单元的最后一次作业,也是本学期面向对象的最后一次作业,在此我将分别对第四单元和整个学期进行总结。
一、本单元的两次作业
第四单元的作业是关于UML的一些处理。UML语言是一种区别于具体语言的一种统一建模语言,也就是说在UML里,你可以只专注于对象的属性、操作、关联、状态、状态变化、时序等,而不需要关心具体的语言细节。而这也就是题目“返璞归真”的由来:在学习了一个学期面向对象以后,我们熟悉了怎么编写一个面向对象的程序,但我们考虑的基本上是对象的属性和操作,并没有关心这个对象的状态、状态转化以及时序,而UML语言却是将对象放在首位,着重于对象的变化、关联等,这才是面向对象的精髓所在。本单元虽然没有在如何画对象的状态图、顺序图上着太多笔墨,只解析了类图、状态图和顺序图,但潜移默化中我对对象的理解加深了许多。
1、第一次作业
第一次作业的架构我分享至讨论区了,本次作业是对UML类图进行解析,我将元素分成了三类:
类和接口:
UML_CLASS
UML_INTERFACE
成员:
UML_ATTRIBUTE
UML_OPERATION
UML_PARAMETER
类间关系:
UML_ASSOCIATION
UML_ASSOCIATION_END
UML_GENERALIZATION
UML_INTERFACE_REALIZATION
然后根据这些UML元素按照层次进行封装,为此我画了个UML类图:
第一次作业按照这三个类来进行数据处理就很容易完成,UML类图如下:
可以看到用这个架构作业层次化分明,写起来容易,debug也简单,第二次作业使用这个架构也能很轻易的完成。
2、第二次作业
第二次作业相比于第一次作业增加了三个需求:输入检查、状态图查询、顺序图查询,其中输入检查是对类图进行输入检查,主要是检查类图的模型有效性,状态图和顺序图则是新的UML图。本次作业特别考察题意的理解,所幸助教及时给了数据规范,不然很难写完。
输入检查
个人感觉输入检查是本次作业难度最大的一部分,循环继承和重复继承的检查实在让人头秃。检查所有的类 / 接口是否有重复继承或者循环继承第一个让人想到的就是递归了。检查循环继承时是用dfs,用一个栈来存储经过的路径,再检查是否经过了同一个类 / 接口。检查重复继承也是用dfs,只是这次是使用HashSet来存储经过的路径,然后检查新路径有没有在HashSet里。递归难就难在想通,想通以后就好写了。
状态图查询
本次状态图的架构如图所示,和第一次作业类似分层存储:
状态图查询主要难在后继对象查询,其实和上述的重复检查差不多,用dfs算法,传入一个HashSet存储所有后继对象,就能得出结果。
顺序图查询
本次作业的顺序图查询相当于凑数的,特别简单,架构如下:
三个查询方法都可以很轻易的完成。
UML总体类图如下:
这两次作业就不进行复杂度分析了,方法实在太多太杂。
3、测试与debug
两次作业我在强测中都取得了满分,这和我的架构层次化有很大的关系,也和我课下写了好多个测试数据有关。
这些数据涵盖了我能考虑到的所有情况,检测出我的程序在递归的时候有些情况会陷入死循环导致栈溢出以及其他一些小bug,才让我的强测取得了满分。本次作业的测试并没有使用Junit,在本次作业中Junit用处不是很大。
二、学期总结
1、四个单元中的架构设计及OO方法理解的演进
经过一学期的OO课程学习,我的OO水平有了很大的提升,对OO的理解也不可同日而语。
第一单元作为OO的入门单元,在第一次作业,我的工作重心却放在了正则表达式上,并没有太关注于面向对象,唯一用到面向对象的是建了两个类:多项式类和单项式类,分别存储多项式和单项式,这个架构没什么问题,只是单项式这个类的设计有问题,我将单项式只当成了幂函数,只有指数系数的那种,在第二次作业中这个架构也好用,把单项式类改成存储系数,x的指数,sin的指数,cos的指数就可以。本以为这个架构可以陪伴我度过三次作业,没想到我还是太天真了,第三次作业的函数嵌套直接让我整个程序毫无扩展的可能。我只能重构整个代码,将单项式细分成因子相乘,再对因子建立一个类,使用了继承才完成。本次作业的架构问题在于一开始没有考虑函数相乘函数嵌套,没有把这些对象抽象成一个类,导致后期的代码重构,总的来说还是对面向对象的理解停留在初级层次。
第二单元是OO最难的单元,这三次作业的架构倒是处理的不错,第一次作业过于简单,我只是当作学习多线程,而并没有怎么引入面向对象的知识。第二、第三次作业则是本单元乃至本学期的难度巅峰。第二次作业稍好一些,用一个电梯类建立电梯线程,控制电梯的上升下降进客出客,因为只有一部电梯就没使用调度器,本次作业的主要难点在于电梯与输入线程的同步互斥问题,在关键的地方用synchronized关键字处理一下防止死锁就好了。第三次作业是所有作业中最为艰难的一次,他拥有着多部电梯,不过有了第二次作业的经验,我只是将第二次作业的电梯new了三部,加入了一个调度器线程,用调度器来控制客人去哪一部电梯,然后电梯线程控制上升下降进客出客。本次作业我对OO的理解有了大幅进步,特别是电梯这个对象太经典了,将日常生活中的东西拿来作为面向对象的作业,使人能够很轻松的把电梯、调度器等抽象成对象。
第三单元则轻松了很多,我们主要学习的按照JML写数据结构,按照规格写方法,个人感觉本单元和面向对象关系不大,更像是一次数据结构复习课。这三次作业主要是处理图的一些查询,而且时间复杂度不能太高。这三次作业就是在保证正确的情况下想尽一切方法降低时间复杂度。在存储路径时,为了使得时间复杂度最低,用的是HashMap和HashSet。在计算最短路径、最低票价、最少换乘、最少不满意度时,用floyd算法直接计算出这些结果,这样在查询路径时可以瞬间出结果而不需等待。本次作业我对OO的理解有所加深,面向对象终究还是需要代码来完成的,而代码必定和算法、数据结构有关,本单元作业用了许多图论算法,在其他单元还使用了递归算法等,面向对象和算法、数据结构始终是分不开的。
第四单元是最后一单元,本次作业则是用面向对象的语言(JAVA)处理关于对象的语言(UML)。这个单元和上个单元有点像,都是完成某些方法。只是这次没有规格,要自己完成各个类的抽象以及封装。这次作业的架构在上面已经详述了一遍,在此就不再赘述。本单元作业中我对OO的理解达到了本学期的巅峰,将那些元素抽象成一个个类,在各个类里完成自己的事情,降低对象与对象之间的耦合性。
2、四个单元中测试理解与实践的演进
学习OO之前:面向评测机测试
第一单元的测试和大一时基本是一样的,构造各种数据,边界数据来测试,前俩次作业的测试主要是测试WRONG FORMAT,第三次则是测试正确性。这个方式有个缺点就是各种函数的组合太多,不可能全部种类测试一遍。
第二单元的测试惨遭滑铁卢,我这几次作业也是构造各种数据然后看运行结果,但是第三次作业输入40条指令之后结果太多了,就没有细看,导致强测爆0,但最后就改了2行代码,这次作业让我明白了测试的重要性,我也学到了如何用python对数据进行定时投放。
第三单元的测试则主要是用的Junit,创建测试程序,判断相应的方法有没有符合JML的规范,在时间方面也学会了估算时间复杂度,估计在评测机上是否会超时,对边数频繁增删的情况进行了压力测试。
第四单元由于UML的方法太多,用Junit过于麻烦,采用了根据每个方法可能错的点构造各种UML类图进行测试,对递归栈溢出问题进行了压力测试。精确地测出了我的程序的许多bug。
总的来说就是,用Junit测试判断程序在某些代码实现上的BUG。自己设计测试集的时候要将可能出现的BUG分类来分别构造测试程序。测试集要考虑到压力测试:运行时间、内存总空间和栈空间。
3、课程收获
- 最大的收获就是学会了面向对象的一些基本方法
- JAVA编程语言
- JAVA编程风格
- 架构设计
- 程序的扩展性和鲁棒性
- 各种测试方法与技巧
- (*南湖决斗技巧)
4、建议
1、OO一个房间里的人太多了,前几次作业还好,可以阅读别人的代码来针对性的找BUG,后面的作业大家的结构各不相同,只能选取一两个人来找BUG,但是看不到被HACK数会无从下手,只能构造几组边界数据来随机hack,这个和设立房间的初衷“学习其他同学的代码的优秀之处、针对性找BUG”可能会有所违背。
2、希望中测数据能够加强一点,中测数据基本上和弱测相当,中测过了强测0分的情况也是存在的。
3、希望强测数据的耦合性能降低,我电梯第三次作业强测0分,但最后只改了2行代码就AC了。
4、希望指导书能够给出清晰的数据规范与作业要求,这个问题在JML和UML单元尤为突出。
5、实验课的设立很奇怪,上午讲课下午实验,而且实验课并没有给出相应的答案或者标程,做完了之后也不知道自己做的对不对。
6、CheckStyle的每行代码数80到三四单元完全不够,课程组给出的接口中随便一个异常类就二三十个字符,一行代码要分成三四行写很难看的。