BUAA OO 第四单元总结&学期回顾
前言
第四单元是本学期OO课的最后一个单元了,愉快的OO之旅即将结束,来看看可爱的助教们给了我们什么内容作为收尾吧。本单元的任务是实现一个UML类图解析器,解析通过StarUML软件创建的UML模型的源文件。虽然比起上一单元的JML来说还是有一定难度的,但是在逐渐理解了源文件中各个元素的结构和存储方式后,完成作业的要求也只是时间问题了。
本单元架构设计
由于本单元的作业是迭代进行的,且我的代码没有经历大的重构,因此架构设计中的UML类图仅展示第三次作业的。
第一次作业
第一次作业是实现一个UML类图分析器。在作业指导书上传到Gitlab后,我便开始着手研究起来。初看上去,这次作业的官方包已经实现了输入输出接口,只需要我们实现一个MyUmlInteraction
类来对输入的数据进行解析,并响应各种指令即可。但当我往下翻,看到那密密麻麻的输入样例时,我的感受是
好在上机实验和学长学姐们的博客给了我很好的启发。通过对上机题目和课上PPT的分析,我很快便熟悉了UML模型文件对各元素的存储方式。由于各元素之间存在一定的联系(例如类的继承,类与属性,方法的包含关系等),因此我自定义了MyClass
,MyInterface
,MyAssociationEnd
和MyOperation
这几个类用于存储这些关系。又由于输入元素的顺序是乱序的,因此我按照层次对输入的元素进行3次遍历,每一次只解析相应的元素,遍历顺序如下:
第一次遍历 | 第二次遍历 | 第三次遍历 |
---|---|---|
UmlClass,UmlInterface,UmlAssociationEnd | UmlAttribute,UmlOperation,UmlGeneralization,UmlInterfaceRealization,UmlAssociation | UmlParameter |
以此建立起一整个UML模型的存储结构。同时也在MyUmlInteraction
类中使用HashMap来保存上述4个自定义的类,便于在输入指令时进行查询。
在实现对指令的处理时,首先通过MyUmlInteraction
类中的HashMap寻找对应的类,并判断是否需要抛出异常,获取了相关的类后,再将指令下降到类内部进行执行,最后返回结果。
第二次作业
第二次作业新增了对状态图和时序图的解析。
总体来说这次作业的处理方法与第一次作业类似,都是自定义了几个类用于存储层次较高的元素,并通过几次遍历来解析相应元素。
时序图遍历顺序:
第一次遍历 | 第二次遍历 | 第三次遍历 |
---|---|---|
UmlInteraction | UmlLifeline | UmlMessage |
状态图由于层次比较杂,我分了5次遍历,顺序如下:
第一次遍历 | 第二次遍历 | 第三次遍历 | 第四次遍历 | 第五次遍历 |
---|---|---|---|---|
UmlStateMachine | UmlRegion | UmlState,UmlPseudostate,UmlFinalState | UmlTransition | UmlEvent |
相关指令同样下降到对应的类中进行查找。
第三次作业
第三次作业在前两次作业的基础上,新增了对模型有效性的检查。
在进行有效性检查时,检查的方法与前两次作业的方式类似,都是通过指令的方式进行的,只不过检查的指令会自动执行。有效性检查涉及到的错误共有8个,涵盖了UML中比较经典的几个错误。我认为比较值得注意的有以下两个:
R001:针对下面给定的模型元素容器,不能含有重名的成员(UML002)
- 针对类图中的类(UMLClass),其成员属性(UMLAttribute)和关联对端所连接的UMLAssociationEnd均不能有重名
这个规则比较容易引起歧义,当时我们在水群里也有过一番讨论,主要集中在”关联对端“是什么和这两者是否需要一起考虑两个问题上。后来基本得出结论,”关联对端“是类(UMLClass)的关联边对面的UMLAssociationEnd,而且这两者是需要同时考虑的。例如如下情况:
class A
{
int b;
B b;
}
class B
{
A a;
}
其中类A中的B类型变量b就是关联对端连接的UMLAssociationEnd的名字,而且从上述代码中int类型的变量b与它重名了,这是肯定不正确的,因此属性名字和关联对端的端点名字是需要一起考虑的。这里我使用了预处理,每次向类中添加UMLAttribute或者UMLAssociationEnd时都判断是否有重名,并将重复的名字存储起来,在检查的时候遍历即可。
R002:不能有循环继承(UML008)
- 该规则只考虑类的继承关系以及接口之间的继承关系。所谓循环继承,就是按照继承关系形成了环。
这个规则里的循环继承可能会出现多个环,需要输出环上所有节点,较为复杂,我的解决方案是定义一个存储循环继承的类的数组,遍历每一个类,只要检测到某个类在环中(通过继承关系找到自己),就只将这个类放入数组中。由于环中每一个节点都会通过继承关系找到自己,因此这一方法是可行的。
其它的有效性检测方法的查找流程也类似,只是部分需要用到一些图搜索算法(数据结构复习题)。
UML类图如下(篇幅限制,没有列出类的属性,方法):
值得注意的是,前面作业中作为小透明存在的CheckStyle这次要大显神威了。由于CheckStyle规定了每个文件不能超过500行,而MyUmlInteraction
类又需要实现相当多的指令,因此它几乎是必定会超过限制的,群里就有很多同学在吐槽行数太高了(血压上升.jpg)因此我将输入处理和模型有效性检查分别拆分成两个类,以此降低行数。
四个单元中架构设计及OO方法理解的演进(附传送门)
第一单元:表达式求导
第一单元要求实现表达式求导,重点在于学习使用面向对象思维进行程序设计。这一单元我认为难度是本学期中最大的,特别是第二次作业在难度上的跃升相当令人震撼。不过第一单元确实是面向对象思想体现得最为明显的一个单元,OO中的封装,继承和多态在其中体现得相当明显。
在架构上,由于我设计的失误以及对面向对象思想理解得不够透彻,三次作业每次都进行了重构,但这几次痛苦的经历也让我对面向对象思想有了更深入的理解,例如在数据存储上,由于表达式子项种类较多,因此可以使用一个顶级父类进行管理。求导是每一个子项都需要进行的,因此可以利用面向对象思想中的多态来完成等等。结合着神奇的递归下降法,最终我还是完成了难度最大的第三次作业。
第二单元:多线程电梯调度
第二单元要求实现一个多线程电梯并模拟其调度过程,重点在于初步了解java多线程原理,并学习对象间的交互。本单元的电梯交互模型主要采用的是生产者-消费者模型,同时还要思考如何实现线程安全(电梯吃人警告),如何提高电梯运行效率等问题。
由于我事先对架构进行了设计,并在一定程度上考虑了可拓展性,因此3次作业没有发生大规模的重构。生产者-消费者模型的运用,让我能初窥多线程乃至于多进程调度算法的一角。对不同对象间的交互,同步互斥算法的设计,也提升了我对面向对象思维的理解。
第三单元:JML模型
第三单元要求根据给出的JML模型实现对于一个社交网络的模拟,重点在于正确理解JML模型的含义,以及复习数据结构算法。这个单元主要是为了让我们理解JML模型的语法特点,只要读懂了某个方法到底是做什么的,就可以实现出来了,实现过程中需要注意使用一些效率比较高的算法,例如并查集,堆优化迪杰斯特拉算法等,防止CTLE。
第四单元:UML类图解析器
第四单元要求实现一个UML模型解析器,重点在于层次化设计思想,以及再次复习数据结构算法。这一单元需要按照层次对UML模型中的不同元素进行划分,并自顶向下依次实现,形成一个存储模型中所有元素的图。
本单元作业的逻辑结构较为清晰,虽然难度较第三单元要大,但是在理解了基本原理的条件下实现起来并不困难。同时,本单元也启发我们在进行程序设计时可以先从大局出发,进行顶层设计,做出大致框架(如在上手写代码前先设计出相应的类图),再根据设计来实现具体的代码。
总结自己在四个单元中测试理解与实践的演进
由于自己能力以及时间所限,在对程序进行测试时没有造评测机,只是通过人肉评测机+构造边界数据+与同学对拍来测试程序的正确性。在第一单元,我主要通过构造一些边界数据来测试程序的正确性;第二单元涉及到多线程以及线程间的交互,如果设计不当可能会出现CPU忙等待或者线程无法结束的情况,因此我学会了使用JProfiler来检查各个方法对CPU的使用情况,以此来配合进行测试。在第三单元里,我初步接触了JUint测试,并尝试进行了一些实践,用来配合测试。第四单元的类图解析器实际上程序间逻辑严密,很多bug甚至可以采用静态代码检查的方法找出来,同时再辅以一些手动构造的样例。除此之外,与同学进行对拍也是一种非常重要的找出bug的手段,因为这样能双方的错误都有可能被发现。
总结自己的课程收获
这门课程叫做面向对象程序设计,虽然没有从中找到对象,但它让我这个学期过得十分充实。
首先便是初步了解了面向对象的思想并通过实践加以巩固。面向对象程序构造可以说是目前比较主流的程序设计方法,比起之前学习的面向过程的设计方法,面向对象将相关的事物和方法抽象成一个整体,并让多个这样的整体各司其职,完成某个工作,逻辑更为严密,也更为自然。
其次,搭建起了多线程程序设计的基本框架。在本学期的操作系统课程中,进程调度是一个重要内容。多进程的系统中相关算法具体是如何实现的,如何避免竞争,死锁等情况的发生,OO课第二单元给了我一些启示。虽然第二单元是多线程,但是其在具体实现某些算法,例如加锁,线程间的唤醒机制等都与多进程有着很大的联系。
再者,部分掌握了java语言的使用。对于我来说,java语言可谓是久仰大名(经常玩的Minecraft就是通过java编写的),通过OO课,我了解了很多java常用的语法,容器,库,同时也掌握了一些相关工具,例如Jprofiler,JML等。
最后,OO课让我戒掉了一些拖延症。精确到天的作业安排,排得满满当当的16周练习(除了有一周放假),让我不敢拖延,也经历了一个充实的学期。说不定假期拖延症又会复发了
几个具体改进建议
1.第一单元第一次和第二次作业间难度跨度相当大,希望在指导书上能给多一些提示,例如提示可以使用递归下降法或者表达式树。
2.希望每次上机课都能给出官方答案或者有相应的反馈结果,没有结果心中也没有底。
3.第三单元的难度不是很大,个人觉得可以压缩成两次代码作业,这样也可以提早一周结束,给考期复习留下更多的时间。