OO第四单元总结-UML

一、作业架构设计

1. 第一次作业

1.1 需求分析

本次作业,需要完成的任务为实现一个UML类图分析器UmlInteraction,可以通过输入各种指令来进行类图有关信息的查询,构造函数的逻辑为将elements数组内的各个UML类图元素传入UmlInteraction类,以备后续解析。学习目标为UML入门级的理解、UML类图的构成要素及其解析方法。

1.2 设计策略

个人认为,第四单元的三次作业中,难度最大的是第一次作业,因为之前的单元虽然也有用到过UML类图来描述程序,但这次作业才是开始正式接触UML的一些具体概念和模型。读官方包里的代码,理解各个UML模型元素之间的关系,最重要的就是理解谁的parent是谁,梳理清楚层层架构,这些花费了我很多时间,但是一旦理解之后,后面的东西就变得简单清晰了许多,后两次作业,也不过是在第一次的基础上进行一定的迭代罢了。

在花了许多时间梳理模型元素之间的层级关系后,我决定将所有的UmlElement模型利用分层级的模式整合到一个大的架构中,所谓层级,就是由parent指向构建起来的。每个层级都构造一个类,利用自己构造的新类,来统一管理本层级原本包括的UmlElement模型。在官方要求实现的解析类MyUmlInteraction中,每个查询方法只要调用管理类的方法即可,具体的数据结构的创建、管理和计算查询,都由管理类来完成。

MyUmlClass,顾名思义,代表一个类,管理类的级别,对应UmlClass类的模型,其中还包括了一个类中的属性、方法、继承关系、关联关系、类关联的对端、接口实现关系等,分别对应UmlAttribute,UmlOperation,UmlUmlGeneralization,UmlAssociation,UmlAssociationEnd,UmlInterfaceRealization等UML模型元素。关于某个类的管理计算和查询,都由该类实现。

其中,关于类的方法,由于它的下面还有一层,就是方法的参数,对应UmlParameter元素,所以再开一个MyUmlOperation类,来管理和存储具体的方法。MyUmlClass类中管理了MyUmlOperation

MyUmlInterface,同样顾名思义,代表一个接口,对应UmlInterface类的模型,同样包括接口的属性、方法、接口的继承关系、关联关系等。

以上是大体的框架和架构,而在具体管理数据时,使用了许多的HashMap来存储,并且通过不同的查询需求和标准,把数据按照不同的键值对进行存储,比如对于类的存储,就使用了三个不同的HashMap来存储,,这样虽然会有数据存储的冗余,但是对于查询模式来说更为方便,能够由给出的条件便捷地找到对应的类,节省了许多时间,可以说是以空间换时间。并且,对于一些需要去重的需求,利用id不会重复的特性,使用的方法存储可以达到去重的目的。

程序具体的UML图如下:

OO第四单元总结-UML_第1张图片

1.3 自己程序的bug

第一次的作业中,在查询类的接口实现时,出现了可能会TLE超时的bug。这是因为接口是可以多继承的,而我在遍历接口的继承时,忘了进行访问位的标记,所以当出现类似菱形图的情况时,会导致已经访问过的点重复递归,时间效率很低,出现TLE。
加上对于访问位的标记visited后,已经访问过的点不再重复递归,从而解决了TLE的bug。这可以说是粗心的问题,也可以说是没有考虑全面的问题吧。

2. 第二次作业

2.1 需求分析

本次作业,在上次作业基础上,扩展解析器,使得能够支持对UML顺序图和UML状态图的解析,通过输入相应的指令来进行相关查询。

2.2 设计策略

这次作业的解析器需要加入对顺序图和状态图的解析,并且需要实现的解析类的名字变为了MyUmlGeneralInteraction。关于类图的解析部分,沿用第一次的设计架构,几乎不用更改。按照分层的思想,原本的顶级类只有MyUmlClassMyUmlInterface,现在加入了新的解析,顺序图需要实现一个MyUmlInteraction类作为顶层类(这也是解析类改了名字的原因),对应UmlInteraction元素,而状态图的顶层类则是MyUmlStateMachine,对应UmlStateMachine元素。

UML顺序图中,根据需要查询的指令,MyUmlInteraction类中只需要包括生命线和消息,分别对应UmlLifeline元素和UmlMessage元素。

UML状态图中,每个MyUmlStateMachine对应唯一的一个MyUmlRegion,就是这个状态机的画布,所以状态图的具体数据实际上是由MyUmlRegion管理的。而MyUmlRegion中又包含了状态机中的所有状态,因为每个状态我们要单独算它的所有后继可到达状态,所以再建立一个MyUmlState类,存储着所有以此状态为source的状态迁移。

和类图相似,在理清楚UML顺序图和UML状态图的架构的基础上,就可以很简单地进行具体的实现。

本次作业程序UML图如下:

2.3 自己程序的bug

因为第二次作业的框架大体沿用了第一次作业,新增的需求也采用了相类似的思路,实现起来没有遇到什么困难,强测也没有出现错误,拿到了100分。

3. 第三次作业

3.1 需求分析

本次作业,在前两次的基础上,没有添加新的查询指令,而是增加了对模型有效性的检查。模型有效性检查部分,将在实例化完毕后自动按序触发执行,不通过指令的形式。且一旦发现不符合规则的情况,将直接退出,不进行后续有效性检查和指令查询。

3.2 设计策略

对于新增的模型有效性检查部分,我开了一个新类MyUmlCheck,在实例化完毕后,专门用来检查模型的有效性。只要按照给出的要求,按照从R001-R008的顺序检查一遍是否违反规则就可以了。

其中,比较重要的是R002、R003、R004这三个规则的检查,分别是对循环继承、重复继承、还有类重复实现接口的检查,这三个操作都涉及到了图的遍历,也是这次作业的坑点,但是,只要搞清楚条件的具体限制,实现起来也并不是很困难。

本次作业程序UML图如下:

3.3 自己程序的bug

这次作业,我在检查循环继承和重复继承时,犯了和第一次作业同样的错误,就是TLE。我对于循环继承和重复继承时图的遍历,没有进行访问位标记,导致出现类似菱形图的情况时时间效率很低,导致TLE。
在使用visited加上对于访问位的标记之后,已经访问过的点不再重复递归,解决了TLE的bug。
这个bug实在是不应该,第一次作业已经出现过了,但是由于这次在思考时的一些漏洞,竟然又出现了这样的bug,太可惜。

二、四个单元中架构设计及OO方法理解的演进

第一单元主题为表达式求导,三次作业分别的要求为多项式求导,带有幂函数、三角函数的求导和最后一次带有嵌套和表达式因子的求导。第一单元算是一次面向对象编程的入门尝试,从第一次作业的基本面向过程编程,到第三次作业时终于有了一些面向对象架构的样子,算是将思维从面向过程到面向对象的一次转换。

在第一单元中,程序架构存在许多不合理的地方。有一些相对独立的功能可以独自拆分出一个类,使代码更简洁清晰。比如,输入字符串的预处理,我是在Main类中直接进行的,可以提供专门进行字符串预处理的类,这样程序的组织架构将会更加合理清晰。而且,第二次作业和第三次作业,我都在前一次作业的基础上进行了重构,这也说明当时我的代码的可扩展性不强,架构设计存在很大的问题。

第二单元为电梯调度,主要训练的是多线程相关的知识。在三次作业的设计结构中,主要架构都是采用的生产者-消费者模式,Input输入作为生产者,Elevator电梯作为消费者,中间采用Scheduler调度器作为共享对象的。第三次作业,还更进一步地采用了Worker-Thread模式,或许可以看做生产者-消费者模式的进阶版。第二单元,由于多线程的特性,开始更加看重架构的设计,通过对一系列编程模式的应用和对具体架构的设计,我对程序架构有了更深一步的理解。

第三单元为JML语言的学习。因为主要看重的还是JML语言,所以架构设计很大程度上是依据课程组的规定,我自己也没有创建什么新的类,更多的地方是专注在了算法的优化和单元测试上。感觉过多精力放在了对图的各种算法的研究。

第四单元的大体架构设计在之前已经作出了介绍。第四单元的作业大量采用了分类、层次化管理的思想,没有急着先写代码,而是在一开始就对程序进行了大体上的架构设计,虽然梳理分层和架构设计的过程比较复杂和费时,但是一旦将架构确定下来后,具体的编程实现就显得容易了许多,也能减少一些出bug的可能性。可以说,面向对象的学习中,将实现模型进行解构分析再组装的过程是一种全新的体验,使我更深刻地体会到了类与类之间的分层、协作关系,对于架构设计的理解也更深了一层。

三、四个单元中测试理解与实践的演进

第一单元的测试,由于刚刚新手入坑,并没有掌握那么多测试方法,所以我一开始的测试方法就是阅读别人程序的代码,第一次作业时程序结构还较为简单,可快速浏览全部代码,之后随着作业逐渐变得复杂,阅读全部代码太过浪费时间也不切实际,我就重点阅读了最可能出现bug的地方的代码。除此之外,在学习了自动测试的相关技巧和测试脚本的编写后,我也尝试了自动化测试的方法,与自己手动构造特殊数据的方法相结合,也算比较好地度过了第一单元的测试和互测。

第二单元,由于是第一次接触多线程,对于多线程的测试,我一开始其实一头雾水。在自己编程的过程中,我采用的主要办法是print,也就是在每个线程相关的操作处打印输出对应的线程操作,这样在测试的时候可以很清楚的看到线程运行的状态到底是什么样的,个人认为非常实用。在测试和互测中,我主要是手动构造一些比较特殊的数据进行组合来测试的,出于多线程的特殊性,也没有找到更好的办法,所以互测找出来的bug也比较少了。

第三单元对于JML的学习过程中,我第一次接触到了Junit单元测试。在三次作业过程中,我每次都写了Junit测试用例,对自己的实现进行了单元测试。在写测试用例时,我是对照着JML规格写的,尽量做到了高覆盖率。这种精细到方法级甚至语句级的测试,对于判断自己的方法实现是否正确是非常有用的。但是,我运用Junit只能做到功能是否正确的测试,不能测试指令非常多的时候的超时问题,这点让人十分头疼。

第四单元由于测试数据形式的特殊性,测试相对来说是比较困难的,因此也没有设置互测的环节。自己进行测试的时候,就需要自己画图来覆盖所有情况,采用了手动构造模型+对拍的方式,把每条查询指令都覆盖到。但是这样有一个问题,就是无法测试TLE的情况,而我正是出现了TLE的bug。

总的来说,经过了四个单元的不断的测试和互测,我对于不同程序的不同测试方法也有了一些理解和掌握。

四、课程收获

之前老师说OO这门课是“昆仑课程”,有很大的难度,现在看来,果然名不虚传。

最大的收获,果然还是一学期的学习下来自己java面向对象编程能力的巨大提升,还有对面向对象思想的深入理解。还有之前已经说过的架构设计能力和程序测试方法的进步。

OO这门课,除了老师课上讲的内容外,如果要想很好地完成每次作业,就需要主动地去寻找大量资料阅读和学习,在这个过程中,本身也是对自我学习能力的一种提升。每一次作业的独立思考、实验课上的紧张感、研讨课上的热烈交流,都是在潜移默化地不断学习和提升,也使我有了很大收获。

五、课程改进建议

1、不知道是否只是我个人的感觉,我觉得几个单元OO作业的难度梯度似乎有点下降的味道?当时写第一单元的时候真的觉得很难很难,尤其是第三次作业,费了好大的功夫,在截止时间前两个小时才通过了。而第二单元感觉稍微简单了,虽然是第一次接触多线程,但只要理解了,作业实现起来并不难。第三单元的难度也主要在图的算法,JML本身其实很简单,第四单元同样也感觉不难。难道只有我一个人这么觉得?是不是可以改善一下难度的梯度,至少变得平滑一点。

2、实验课希望可以在课后公布一下正确答案和成绩,否则都不知道自己到底对了没,对于学习的知识掌握程度自己心里也没个底。

3、研讨课或许可以再提高一下门槛。前期还好,帮助还比较大,到了后期的时候,研讨课的含金量就稍显不足,感觉没有什么特别有价值的东西。而且也不知道是不是因为线上授课的原因,感觉研讨课上大家的参与度不是很高,或许可以考虑换个形式,比如分组?

六、线上学习oo课程的体会

特殊时期,采取线上授课的方式,也是很新颖。所幸OO这门课本身的自学成分就居多,而且平时作业和实验什么的本来就是在网上进行的,所以并没有很大地影响到教学质量。作业还是压力山大,实验课也一样紧张刺激。可能受了影响的就只有研讨课吧,我听别人说研讨课本来是可以请人来办讲座的,现在只能同学们自我研讨,质量可能有所下降吧,而且同学们的积极性也有一点点下降。

线上教学最大的好处就是时间可以自由分配,在这一点上其实我还是比较喜欢的。

也不知是不是线上教学带来的影响,感觉理论课的内容有点过于抽象,只听老师上课讲的东西还是很难弄懂(感觉讲了又感觉没讲),只有自己写作业的时候具体实践了才有比较好的理解。

总的来说,这一学期的线上学习体验还是不错的,经过了OO课程的洗礼,也学到了许多有用的东西。虽然OO课程到此就告一段落了,但后面还有更大的挑战在等待着我们,今后还要更加努力才行啊!

你可能感兴趣的:(OO第四单元总结-UML)