BUAA_OO_2020_Unit4_Overview
架构设计
由于本单元仍是增量开发,因此先给出最后一次作业的UML图(在新标签页打开图片以查看全彩高清无损大图):
本单元我的作业是以适配器模式为主进行实现的,将官方包所给的UML元素封装为了便于使用的类型。
分析和评价
第十三次作业
第十三次作业实现的部分如下:
由于第13次作业中的所有查询方法都是关于类图的,都围绕UmlClass
元素展开,因此我的设计是将各个查询方法的主体逻辑部分都在MyUmlClass
中实现,而MyGeneralInteraction
类只调用MyUmlClass
类中的各个方法。这一次作业的各个查询逻辑都比较简单,只有查询实现接口时用到了bfs,其他的基本都能在O(1)~O(N)时间内解决。
一个处理得比较特殊的地方是UmlOperation
的参数类型的表示方法,每个方法需要两个布尔值来表示其有无参数,以及参数的出入方向。如果用两个布尔量来表示,每次判断参数类型时要进行两次查询,开销比较大且代码冗长,因此我使用了一个int
来表示,0x1
位表示参数方向,0x2
位表示有无参数。这样,只需要与或运算便可以判断方法的参数类型,且只需进行一次查询,使代码更加简洁。除此之外,为了方便对继承关系进行处理,我的MyUmlClass类与MyUmlInterface类实现了Generalizable接口。
本次作业的代码行数统计如下:
NUMOF LINES FILENAME 13 lines src\common\MyOperationQueryType.java 310 lines src\elements\MyUmlClass.java 23 lines src\elements\MyUmlElement.java 69 lines src\elements\MyUmlInterface.java 90 lines src\elements\MyUmlOperation.java 55 lines src\elements\MyUmlOthers.java 6 lines src\interfaces\Generalizable.java 9 lines src\MainClass.java 260 lines src\MyUmlInteraction.java ----------------------------------------------- total : 835 lines.
整体来说代码量不算很大,并且由于许多重写方法(hashCode,equals等)的存在,实际上的主体逻辑部分不是很多。
在bug方面,本次作业在强测中未出现bug。但有的同学在用HashMap替换ArrayList时没有处理好重名的问题,出现了bug。
第十四次作业
第十四次作业实现的部分如下:
增加的部分主要是状态图与顺序图的查询部分,也基本上没有太大难度。主要是增加了多种UML图的元素类型,与上一次作业不同的是这次的查询逻辑分散在了几个类中,同样是调用相应实例的方法即可。
本次作业的代码行数统计如下:
NUMOF LINES FILENAME 13 lines src\common\MyOperationQueryType.java 310 lines src\elements\MyUmlClass.java 23 lines src\elements\MyUmlElement.java 99 lines src\elements\MyUmlInteraction.java 69 lines src\elements\MyUmlInterface.java 65 lines src\elements\MyUmlLifeline.java 90 lines src\elements\MyUmlOperation.java 55 lines src\elements\MyUmlOthers.java 82 lines src\elements\MyUmlState.java 97 lines src\elements\MyUmlStateMachine.java 6 lines src\interfaces\Generalizable.java 9 lines src\MainClass.java 423 lines src\MyUmlGeneralInteraction.java ----------------------------------------------- total : 1341 lines.
比上一次作业增加了近一半,但实际上仍然是因为存在大量的重写方法,主体逻辑并没有增加许多。
本次作业在强测中出现一个bug,是当UmlMessage的一个末端是UmlEndpoint时会在进行类型转换时发生错误,导致程序意外退出。出现的主要原因是测试时没有考虑到这种特殊情况,程序的鲁棒性也不是很好。(事实上,如果直接在初始化方法中加入一个try-catch块即可避免这个问题,但在写作业时因为过度相信程序逻辑的严密性而没有加)
第十五次作业
第十五次作业的UML图如本文开头所示。这次作业增加了UML图正确性检查,为了方便对UmlClass与UmlInterface进行处理,我增加了抽象类MyUmlClassOrInterface,与官方包中的UmlClassOrInterface相对应。但实际上它的功能和Generalizable是一致的。
本次作业的主要逻辑在MyUmlChecker类中实现,在编写的过程中,由于在前两次作业中将某些元素处理后即舍弃,没有考虑到后续会对其利用的情况,在这次作业中又重新实现了对一些元素的处理方法。在最初写作业时因为对指导书的理解不是非常透彻,在一些方法的实现上纠结了一段时间,最后考虑到比较宽裕的CPU时间限制,我均选择了最稳妥的方法,也就是遍历每个元素并挨个进行广搜。除了R002~R004需要bfs外,其他方法均比较简单。
本次作业的代码行数统计如下:
NUMOF LINES FILENAME 13 lines src\common\MyOperationQueryType.java 341 lines src\elements\MyUmlClass.java 8 lines src\elements\MyUmlClassOrInterface.java 23 lines src\elements\MyUmlElement.java 99 lines src\elements\MyUmlInteraction.java 97 lines src\elements\MyUmlInterface.java 65 lines src\elements\MyUmlLifeline.java 90 lines src\elements\MyUmlOperation.java 55 lines src\elements\MyUmlOthers.java 86 lines src\elements\MyUmlState.java 97 lines src\elements\MyUmlStateMachine.java 6 lines src\interfaces\Generalizable.java 9 lines src\MainClass.java 206 lines src\MyUmlChecker.java 486 lines src\MyUmlGeneralInteraction.java -------------------------------------------------- total : 1681 lines.
又增加了300余行,主要是加入Checker造成的。
在中测的过程中,我的Middle5最初没有通过,我发现R005中未处理Interface的属性。但修改完仍不对,最后发现是R001中将AssociationEnd的reference的name当做了其本身的name进行处理,修改后通过。强测结果尚未公布,因此bug情况未知。(6.14更新:本次作业亦未出现bug)
课程中架构设计及OO方法理解的演进
- 第一单元中我的思维从面向过程式逐渐转变为了面向对象式,并理解了工厂模式和层次化的结构设计方式,理解了继承和多态等概念,抽象能力也得到了很大的提升。
- 第二单元我理解了多线程的知识,并体会到了增量开发的便利性,从此我在设计一个程序时能够考虑到以后拓展开发时的需求,提前为拓展做好准备。
- 第三单元主要的收获是理解了契约式设计对程序质量的作用,并明白了调用者和被调用者需要遵守的准则,学会了用SOLID准则保证程序质量。(并学习了一些新的数据结构和算法,如并查集、tarjan等)
- 第四单元我主要理解了代理模式与抽象在面向对象中的作用。并了解了UML图的一些规则,有利于作出更规范的UML图。
在架构方面,第一单元主要是从普通的面向过程编程变为了层次化的设计,层次化在这一单元的体现也十分明显。第二单元作业主要是按功能分工进行了划分,从行为角度上来说,也是层次化的。第三单元作业主要是实现接口来完成功能,架构比较单一。
测试的理解与实践的演进
通过OO课程,我发现,程序的可靠性(运行的稳定性与结果的正确性)一半靠科学的架构设计,另一半靠全面的测试。与先前的算法课程相比,OO课程的需求更为丰富、场景更加多样化,因此样例的构造变得更为繁琐。得益于前代学长的帮助以及几个队友的通力合作,我从第一单元的第一次作业开始便开始使用自动化测试程序,来构造数据并进行测试(对拍或与标程进行比较等)。除此之外我也尝试过JUnit测试,JUnit的工具链十分完善,也很友好,但构造全面、强力的测试数据并不是一件简单的事情,因此我仍然主要依靠自动评测来进行测试。
当然自动评测也有其本身的局限性,随着需求的越来越复杂,使用自动评测程序进行数据的构造也变得越来越艰难。例如本单元的最后一次作业,利用自动评测程序构造各种各样的特殊情况的确很困难,因此最后一次作业的测试强度并不充足,因此我对自己程序的信心并不是十分充足。可以说这是自动化测试带来的弊端之一。
OO课程的收获
感觉我在OO课程中得到的收获是上大学以来前所未有的。通过OO课程,我的工程化能力得到了显著的提升,不仅仅是编写代码的能力,架构设计能力、以及代码编写的规范性都有了很大的提升。除此之外,借由此课程而掌握的Markdown、git、Java等都是极为有用的工具,受益的不仅仅是OO课程本身。
当然,这门课程带给我的压力也是非常大的。让我印象十分深刻的是第一单元第三次作业的debug过程,虽然经历了很曲折的过程,但最终依然是留下了一点小bug,这对我来说是十分宝贵的经历,在之后虽然也遇到了相似的情况,但我的心态变得更加坦然了。虽然说后两个单元的作业难度不算大,但在每次进入强测前,我也是提心吊胆,如履薄冰地对程序进行了许多测试。
总而言之,OO课程作为“昆仑课程”是名副其实的,我认为我从OO课程中学到了许多,并相信OO课程会成为我大学期间的珍贵回忆之一。
针对OO课程的改进建议
OO课程的整体体验是非常好的,但我认为还有一些地方有待改进:
- 首先是第三单元的作业设置,我认为不够合理。虽然“根据JML完成代码”本身没有什么问题,也体现了契约式设计的思想。但这一单元的作业仍然略显空洞,最后为了增加区分度几乎变成了算法测试,我认为这是舍本逐末的。并且,JML虽然严谨,但它和实际应用略有脱轨,从同学们的反映来看也是如此——JML工具链不够完善,也鲜有实际开发场景使用JML。我认为,学习契约式设计时,十分重要的一点是明确在实际开发中的职责划分,即函数的编写者对调用者应作出怎样的承诺,调用者需要遵守函数编写者的何种限制。因此我建议这一单元的形式可以不变,但要将JML改为Javadoc,并分别让学生以调用者与被调用者的身份进行开发,强化约束、淡化算法,这种形式或许比“披着JML外衣的算法作业”效果要好。
- 其次是关于实验课的题目,我们理解助教在准备实验课的过程中同样花费了许多心血,但也许是因为实验课形式的原因,在实验课的过程中我总会感到“摸不到头脑”。例如一些设计模式的问题,完全可以提供一个比较简单的场景、比较简单的问题,用编写代码的形式来理解设计模式;又如当时的“JVM垃圾回收问题”,虽然题材十分新颖也很有用,但作为练习单元测试的实验并不是十分合适,要练习单元测试完全可以给出一个类的接口(而不是实现),让学生编写单元测试代码,以单元测试对实际程序中bug的覆盖率作为评测标准,而不是利用单元测试来发现bug,修改程序。从同学们的反馈来说,这对练习单元测试来说效果并不好。总而言之,还希望实验课的助教可以考虑更加贴合教学目标的实验课内容。
- 最后是关于研讨课的形式,根据我本人一个学期以来听研讨课的体验,总体来说是“要么觉得价值不大,要么觉得难以跟上”。例如,在研讨课上分享架构设计,可能大多数人的架构设计都大同小异,此时再进行分享就略显重复;在研讨课上分享一些比较冷门或难以理解的设计模式或Java本身的知识,效果就不如在讨论区发帖要好。我觉得研讨课应该更多的是大多数同学的问题交流,或同学们关于老师教学内容的交流,经验分享一类的许多都可以用讨论帖的形式呈现。(而且也可以避免为了恰分而分享一些没有用的东西)
线上学习OO课程的体会
感觉起来,相比于其他课程,OO课是受到线上教学形式影响最小的一门课了,(虽然没能见到老师和助教们比较遗憾),作业、讨论等都在线上完成,过程也比较流畅。总体来说,我个人并不喜欢线上教学的方式,在课堂上的学习效果要比在家里好太多。