UML——BUAA_OO 第四单元总结 & 课程总结
UML——第四单元总结 & 课程总结
第四单元架构设计
第一次作业
本次作业要求是实现一个UML类图解析器。
说实话,最开始我看到指导书上要求的时候毫无头绪,尤其是那一堆测试样例,真心看不懂。于是听从助教建议,开始看官方下发的源代码,但是真是应了那句话“所有的程序员都讨厌两件事,1别人不写注释2自己写注释”,里面一个注释都没有,理解起来很困难,遂放弃。走投无路,被迫无奈,开始手动实现测试样例,将类图完整复现出来,“复花数小时,豁然开朗”,类图复现出来之后甚至觉得这次作业基本上已经完成了,总结下来这样做的好处有如下几点:
- 对样例有更加直观的理解
- 对类图层次结构有了清晰的认识
- 不用阅读源码,看到这个类就知道它一定有某些方法,作用是什么
此次作业的架构设计并没有什么特殊之处,就是简单的实现了接口,所有的功能都在实现接口的类中实现(可能是JML后遗症),接口中函数的实现大都通过暴力查询的方式来解决,但是需要注意方法复杂度,尤其深搜和广搜查询的时候要做标记,否则会超时;由于此次的解析器只涉及一个类图,所有并没有添加新的类,导致代码行数正正好好500行,可谓臃肿极致,这也导致可拓展性几乎为0(以此为戒)。
UML类图:
![](https://img2020.cnblogs.com/blog/1961317/202006/1961317-20200615143356372-5961550.png)
第二次作业
在第一次作业的基础上,要求实现时序图和状态图的解析。
对于时序图和状态图的理解方法,同样采取了和第一次作业同样的策略,有了上次的基础,所以这次速度快了很多。
由于上次作业的架构十分不合理,所以在这次作业中创建类HerUmlSequenceDiagram
来管理时序图,创建类HisUmlStateDiagram
来管理状态图,创建类YourUmlClassDiagram
来管理类图。在类WhoseUmlGeneralInteraction
的构造方法中通过区分不同类的类型把一系列UmlElement
子类加入不同图中。在三个图中实现接口中函数功能,WhoseUmlGeneralInteraction
类只负责调用,各部分的代码量适中,结构较为清晰,有较强的可扩展性。
UML类图:
![](https://img2020.cnblogs.com/blog/1961317/202006/1961317-20200615120250235-652390405.png)
第三次作业
在第二次作业的基础上,要求实现图一致性检查。
和第二次作业相比,只增加一个类CheckForUml
负责对三种图进行检查,所需要的数据由三个管理图的类通过传参的方式提供。优良的可拓展性在实现这次作业中得到了充分体现,我几乎没动过原来的代码,不需要重构,要做的的就是添加新内容。在实现的过程中我们可以用一些小技巧来降低复杂度减少出bug的概率,比如我们在实现checkForR002()
检查是否有接口和类循环继承情况出现时可以顺带着把checkForR003
检查接口是否重复继承的功能实现,简单来说就是在搜索的时候看一个接口是否被搜过,如果被搜过则说明重复继承。
UML类图:
![](https://img2020.cnblogs.com/blog/1961317/202006/1961317-20200615120737245-2064174111.png)
架构设计及OO方法理解的演进
正所谓“万物皆是对象”,对于一个现实中的需求或者是问题,我们总有办法将它拆分成几个部分,每个部分处理好自己分内的事情,最后通过所有部分之间的交互共同实现需求和解决问题。
同样面向对象的设计模式其实离不开面向过程,我们的最终程序总不能像UML图一样标清楚类之间的关系以及属性名和方法名就把问题解决了吧。其实上面一段话中所说的“每个部分处理好自己分内的事情”在我看来就是面向过程,例如第二单元的电梯类,如何实现将乘客运送至目的地就是面向过程。
而OO课的终极目标我想就是让我们在设计的时候就对整体进行把控,先不考虑细节,从更高层次进行系统建模,理清类的职责和类之间的交互,直至最后将过程部分实现,补全细节,一份好的代码问世。
第一单元
最开始就遇到了最考验架构设计的单元。此单元我的架构设计不是很清晰,无法满足迭代开发所需要的良好可扩展性,可以说是次次重构,极大的增加了工作量;同时对类的职责划分也不明确,在第二次作业中我居然在Main类中实现求导结果的化简,虽然不能说这样做就是不对的,但是经过一个学期的OO磨练真心认为这种做法很蠢,单独实现一个化简类或者在表达式类内部实现都可以,就是没必要采用这种下下等做法;此外我对类之间的继承和接口的使用十分不熟练,导致各部分耦合度很高,时时刻刻想着“高内聚,低耦合”,等到实现的时候味道就都变了。
第二单元
这一单元主要考察多线程。当然一个好的架构也是很重要的,可以减少出现死锁等bug出现的概率。经过上一个单元的洗礼,在第一次作业中就实现了一个不错的架构,在之后的作业中也没有重构。
多线程部分最重要的就是控制好各个线程对临界资源的访问。我主要采用了生产者和消费者模式,请求队列作为临界资源受到synchronized
关键字限制,调度器管理请求队列并接受来自输入线程的请求,再将请求分发给各个电梯。这样做主要有两点好处,破坏了循环等待条件从而避免死锁产生;其次是易于实现调度策略,将调度算法分成两部分,分别是调度器分配算法和电梯运送算法,对性能的提升有很大的帮助。
第三单元
这一单元主要是JML规格。这一单元的架构设计的重要性体现的不明显。即使是只用到了实现相应接口的三个类也没问题。
JML的作用就是用规范化的语言实现对类内部过程的描述和类之间交互的描述,可以减少歧义;同时JML可以帮助我们暂时忽略细节,从架构和类之间的交互层面进行设计,推迟过程的实现。
但是在实现的时候需要注意不能机械翻译!
第四单元
这一单元主要是UML图示。和JML作用比较相似,就是从架构设计上进行思考,推迟面向过程的思考,但相较于JML,UML的思考层次似乎更高了一层,接口和类的继承,类与接口之间的实现关系等,而这些都是JML所不能实现的。
这一单元我认为重点不在于作业代码的架构设计,而在于通过理解UML图的结构从而实现上述OO课程的终极目标。
经历了一个学期OO课程的洗礼,使我对面向对象编程思想有了一定的理解,在编写代码时我也会在架构设计和系统建模层次做更多的思考,尽量做到类职责清晰和“高内聚,低耦合”。同时也要考虑到后续的迭代开发,从而采用合适的数据结构来管理数据。
在前一段时间我帮了一个高中同学完成了Java大作业(我认罪,这是一种不好的行为),要求就是实现一个汽车租赁系统,因为在架构方面就做了较多的考虑,结构很清晰,没有出现大规模删改现象,完成的比较轻松比较令人满意。在这次的经历中我从OO课程中学习到的思想和方法得到了充分的体现。
测试理解与实践的演进
证明程序正确,1w条数据ac也不够;但是想证明程序错误,1条出锅的数据就足以———HansBug
测试的重要性不言而喻。
第一单元
对于数据,采用手动+Python程序自动生成的方式;对于结果的判定,因为Python有现成的求导函数,所以只需要再写一个判定程序将自身程序生成的表达式和Python函数得到的表达式进行对比(数字代入的方式),判断正误;最后通过shell脚本实现自动评测。
第二单元
第二单元除脚本外全程采用Java实现,数据生成采用随机数,输入采用线程输入(现学现用),通过睡眠达到间隔输入的目的,最后将输出结果送入评测机,评测机的判断方式主要有判断People上下楼层是否正确、电梯必须在规定楼层运行不能跃层等规则,评测效果比较令人满意。
第三单元
第三单元有了工具链的帮助,采用Junit可以实现对方法的评测,十分方便。同时也和同学采用对拍的方式通过随机数据进行验证。
第四单元
开局啥都没有,装备(数据)全靠手打。通过手动构建UML图并导出数据,采用对拍或者干瞪眼的方式进行验证。
相较于上学期的计组对拍器只是一个用C语言实现的简单字符串对比,这学期的测试明显有了较大的进步,学会了运用不同的工具,学会了简单shell脚本的编写,减少了多次事故的发生。
课程收获
- 学习到了面向对象编程方式,学会从系统建模开始思考
- 学会了借助多种工具进行自动评测
- 进一步认识到了数据结构的重要性,选好合适的容器很重要
- 对待自己的代码不能像欣赏艺术品一样只看到好的方面,要严格要求,正如助教所说“什么样的程序应该得什么样的分”,要学会挑毛病,架构设计符合要求吗?做到“高内聚,低耦合”了吗?复杂度符合要求吗?经过充分测试了吗?bug都修复了吗?等等,虽然一些方面在OO课程中并不影响分数所以可能没有得到充分的重视,但是在日后的产品设计中上述的这些都很重要,同样是“什么样的产品应该得到什么样的评价”。
给课程组的建议
- 建议压缩第三单元的作业量。第三单元有关JML语法的部分其实比较少,而且因为同学们有离散数学的基础所以理解起来也不是很困难,布置三次作业完全没必要,尤其是第三次作业完全就是在考算法(顺带说一句题外话,建议增加大一数据结构课程难度),个人认为偏离了主题。而且加上JML本身并不是特别的普及,网上搜索关键字"JML"全是OO第三单元博客,自身的工具链也不完备,所以建议减少第三单元作业量为其他内容腾位置。
- 建议调整单元顺序为3->4->1->2。个人理解对于第三单元的JML和第四单元的UML的核心目的,就是让我们尽可能在设计阶段就对全局有一个把控,尽可能推迟我们面向过程的思考,而不是写到哪算到哪。所以如果同学们学习完第三四单元达到了学习目标之后,再完成第一二单元的作业,相信大部分同学作业的整体架构一定比他们之前设计的要好(至少我是这样,说实话我都不忍心回头看我第一单元的代码,如果让我现在再重新设计一遍整体架构一定会比之前好)。此外我觉得所有作业中最难的是第一单元的第三次作业,把难度最大的作业放到初期也有一些不太合适。
- 建议降低第三单元实验难度。第三单元的实验是所有单元中最难的,尤其是第二次实验,阅读大量代码仅依靠说明图示和少量的JML规范找到并修复所有bug,而且bug原因、位置、数量都不明确。在我们平时的作业中,看自己写的代码找到并修复bug在短短的两个小时内都很难完成,所以实验的难度可想而知。
- 建议加大研讨课分享门槛。从本班级的研讨课内容来看,个人认为前两单元的分享都很精彩,在我的脑海里留下了很深刻的印象,内容独特,有自己的理解。但是到了后面两个单元的研讨课,分享的质量和前面相比着实低了不少,很多分享内容就是一些简单的资料整合(搬运工的工作),缺少自己的理解,听完之后感觉没什么收获。
- 如果可能的话,缩减第三单元的作业,是不是可以增加UML和JML结合的作业呢?(突然冒出的想法)
完结语
在本学期的课程中,我认为线上学习对OO课程的影响是最小的了(隔壁OS是真滴惨,课上测试都没了),无论是作业提交方式或者是实验课都没有影响,而且录播课有一点好处就是有不懂的地方可以反复观看,效果很好,研讨课采用腾讯会议的方式比较方便,同学们讨论很积极。遗憾就是没有见到各位老师和助教,感觉很亏。
一学期的OO课程至此画上了句号,收获真的很大,课程体验也很棒。也感谢老师和助教们为打造高质量的OO课所作出的努力。