一、本单元架构设计
第一次作业
本次作业要求解析UML类图。
首先,将UML中的各个元素(比如UmlClass、UmlInterface等)转化成自己定义的类(MyClass、MyInterface),目的是为了进行更好的管理,将UML中分散的元素和结构变成人能够理解的形式。此时,每个MyClass中包含属于自己的方法,每个方法都有属于自己的参数,这样在进行查找的时候简明易懂。对于数据的管理,我首先建立了一个Container类,负责管理各种List和Map。对于一些常用的元素,我采取了HashMap和ArrayList并存的方式,既能做到快速遍历,又能做到快速查找。
但是,由于忽略了接口的多继承,所以在bfs搜索的时候并没有进行标记,导致强测CTLE了一个点。这个问题我在构造的时候曾经想到过,但是当时是对于Class进行考虑的,而Class则不会出现这种情况,因此没有引起重视。这说明我在写代码时具有敏锐的直觉,但是在进行分析的时候不够细致和严谨。
总体而言,对于本次作业的架构设计我是非常满意的,符合高内聚低耦合原则,也满足可读性原则,同时具有良好的可扩展性。
第二次作业
本次作业加入了状态图和顺序图的解析。
本次作业代码是在第一次作业的基础上迭代而来,新增了StateContainer以及SequenceContainer两个类用于管理和状态图以及顺序图相关的变量。本次作业进一步注重了效率,合理利用了强测数据的各种限制来最大限度的提高效率。(例如,当发现重名时直接做好标记,一旦查找时不必再次遍历可以直接抛出异常)
在架构上,MyUmlGeneralInteraction类仍然只是作为调用者去调用响应容器里的对应功能,它只由一系列try catch组成而不负责繁琐复杂的具体过程,这样非常简明,也将责任交给了查找的类,符合单一职责原则。
第三次作业
本次作业加入了检查机制。
本次作业的代码仍然是在上一次作业的基础上迭代,没有删减只有增加,新增了MyChecker来进行格式检查。MyChecker需要从各个容器中获取必要的信息(比如数据相关的Map和List),并且逐一检查后将结果反馈给MyUmlGeneralInteraction类。这样的架构简单易行,同时在检查的时候也容易发现错误。
由于本单元三次作业没有重构,后一次作业均是在前一次作业的基础上添加而来,故只展示第三次作业的相关信息。
代码规模
类图
二、四个单元中的架构设计以及OO方法理解演进
第一单元
求导是我第一次真正意义上写面向对象的编程,虽然我没有很好的做到高内聚低耦合的原则,把太多的内容放在了Handler里,但是我没有经历太大的重构,这说明一开始的架构是成功的。当时的设计是MainClass作为总的控制程序,Term类负责储存每一项有关的信息,Handler负责读入和求导,在此后的两次作业虽然由于项的内容在不断改变导致存储所用的数据结构在改变,同时求导在不断变化导致Handler里有较大改动,但是这些都是具体实现细节的改动,是可以接受的。对于第三次作业,由于我不太清楚如何利用面向对象的方式来解决这道题,只好面向过程地写了一次递归下降分析,同时又用递归下降的方式对输出优化了一次,但是优化效果仍然不太好,因为我的程序实质上没有“理解”和“读懂”式子,而只是机械的进行形式上的化简,这样必然导致无法化到最简。要想在性能分上得高分,势必要从架构入手,在求导时就进行化简,这样能够更加简明的达到更好的效果。
第二单元
本单元的主题是多线程。总体而言,我的设计都是基于生产者-消费者模型来完成的,Scheduler相当于模型里的托盘,ElevReader相当于模型里的生产者,Elevator相当于多个消费者,为了避免死锁,我将一切与刷新乘客等待队列有关的操作全部放在Scheduler里进行,这样虽然避免了死锁,但是在运行效率上可能会降低。此外,由于我三次作业采用了三种几乎完全不同的调度算法(第一次采用SCAN算法,第二次采用无脑争抢顾客的算法,也就是没有算法,第三次采用了自行设计的非常合理、性能分真的很高的算法),因此在Scheduler里大改了调度器。但是,这仍然可以视为局部的调整而不是整体的重构。在本单元,我已经能够较好的掌握面向对象的思维方式,总体设计也比较符合高内聚低耦合的特点。
第三单元
这个单元与其说是考验架构设计,不如说是用来理解别人的设计。本单元的官方包已经设计好了架构,只需要自行填充和完善功能即可,但是通过这一单元的学习,我明白关于功能的描述和功能的具体实现可以是两个完全不同的东西,有时光从字面的描述难以直接得出实现的方法,需要自行设计算法来解决问题。通过这一单元的磨练,我对于抽象问题的理解能力加深了。
第四单元
本单元考察的是UML的解析。这个单元的架构设计非常合理,处处体现着我已经能够熟练掌握面向对象程序设计的技巧与方法了。通过实现自己的类,我把UML里的各个元素之间的关系很好的梳理出来,接下来只需要按部就班地按照题目要求解决问题即可。本单元没有重构,下次的作业只是在上次作业的基础上新增了查询接口,设计较为成功。
三、四个单元测试理解及实践的演进
第一单元的测试主要靠人工数据加python对拍。对于人工数据,主要是进行一些边界测试数据,比如容易引发正则表达式超长回朔、容易引起数组爆掉等极端情况;python主要是通过生成大量数据来进行普遍性的测试,首先用随机数生成测试数据,然后用命令行导入Java程序,将输出结果与sympy的生成结果进行比对并判断正确性。
第二单元的测试我采用了针对性测试。在生成的数据中,有的数据倾向于短时间高负载,这样可以检查出线程不安全引起的超载;还有的数据倾向于低密度长间隔,这样可以检查唤醒机制是否完善;还有的数据倾向于卡调度算法,针对自己的调度算法生成一些对自己调度算法不利的数据......通过分类而全面的黑盒测试数据,我基本排除了Bug。但是由于自己对题意理解出现了偏差,所以生成的数据不会有我误解题意的那部分,导致互测时被hack,这给予我的启示是要多和同学交换对拍器来减少这种因为误会题意而产生的问题。
第三单元我采用的是对拍加人工比对检查代码。用python随机生成一些数据确保没有大问题以后,剩下的就依靠肉眼比对来确保正确性。这样一种验证是形式化验证,理论上比黑盒测试来的更加完备。
第四单元的测试我并没有想出如何自动生成UML表达式,于是采用手捏数据来判断正误。这个单元我做的测试比较少,不过还是覆盖了我认为的易错的情况,但是由于我对于存储容器掌握熟练,遍历查找等基本功比较扎实,也没有出现较大的问题。
四、课程收获
首先,OO课让我实现了从面向过程到面向对象思想的转变。由于面向过程的代码在面对纷繁复杂的大问题时比较难以分解成为一个个小问题,同时在debug时也难以快速定位,所以学好面向对象非常重要。通过这门课,我学会了如何设计架构并且构建合适的类,我学会了在写代码时考虑可扩展性和代码整体风格。
此外,我掌握了一些实用的知识。比如说,在多线程那一单元我学会了包括生产者-消费者在内的多个模型,写出了自己的多线程代码,掌握了多线程代码的调试方式,这些在以后的工作中都会有用的。此外,我还学习了JML,这使我拥有了给他人准确下达指令以及准确理解他人意图的工具。
其次,我的工程能力提升了。在独自一人书写上千行的代码时,如何协调好局部与局部的关系,如何在性能与代码的可读性间进行权衡,如何设计出良好的架构等问题只有通过一学期高强度、魔鬼般的训练才能亲自体会并给出自己的回答。工程能力的培养不是一朝一夕就能完成的,需要经年累月的积累,而OO课无疑就是一个很好的提升过程,通过不断地迭代和限时训练迅速提高了我把控代码的能力。
最后,这门学科给予了我跨学科的思考和启发。例如在做测试时我不断思索哪种测试最有效?是黑盒测试、白盒测试还是形式化验证?再结合OS讲的关于操作系统安全性检查的内容我有了自己的答案——形式化验证,尤其是数学上可以证明的形式化验证才是最严谨的。其次,OO在多线程部分也让我更深入理解了OS课程部分的多线程以及死锁问题。此外,它还帮我复习了一遍数据结构学过的知识,包括但不限于迪杰斯特拉算法及其堆优化,哈希、链表等功能。OO虽然是一门自成体系的课,但是这并不妨碍它与其他专业课产生紧密联系,由此引发的思考也是我的巨大收获。
学完一学期的OO,我从当初的一无所知,变成了现在的理解了多线程、设计模式、UML等一系列可能跟我未来有很大关系的知识。同时其魔鬼式的作业也让我的工程能力有了很大的提高。
五、改进建议
-
实验课做完了以后没有任何反馈,犹如石沉大海般从此没了音信。建议在实验结束后重新开放题目并公布标答,让我们明白自己的错误出在哪里。
-
虽然知道助教们非常辛苦,但还是希望助教们尽可能的严谨一些。例如第三单元的指导书,在不少地方有逻辑不严谨的地方,导致官方代码发布后频繁更改,没有及时更新官方包的同学容易出现错误;此外,第三单元还出现过中测刚开始时存在错误需要重测的情况,这也应该在开放中测之前进行预防;还有就是第四单元最后一次指导书,对于各种错误的说明不够详细,导致讨论区一片提问。
-
不得不说,选择求导单元作为面向对象设计的第一课简直就是一种灾难。首先,那时候刚学习OO的我们根本就不懂得面向对象编程的内涵,不懂得如何设计架构,不懂得如何自行设计出各种类,不知道高内聚低耦合的原则,那时候的我们几乎只会把java当C语言用。更好的方法是先用JML让大家熟悉如何设计架构、如何合理利用各种容器,然后再把求导这种对设计要求较高的单元放出来,这样可以有效提高大家的代码质量。
-
助教们没有提供标程。提供标程有利于我们理解设计者的设计思路,同时标程中运用到的一些小技巧有利于我们学习,没有标程的话虽然也能写出来,但是无法使得代码更加优雅、更加自然。提供标程有利于同学提升自己的代码层次,从中学习到自己欠缺的部分。
六、线上学习体会
线上学习对于OO这种课来说非常合适,首先这门课在课上老师几乎没有讲什么,基本靠自学(虽然有些直接但这是大实话),二倍速过去不也挺好的嘛,省事又省力。此外,作业和实验课不受影响,因为即使返校上课也是这种形式。至于研讨课,大家对于研讨课的积极性普遍较高,采用腾讯会议也能保证视频质量,所以影响并不大。
最后,感谢助教们一学期的辛勤付出,希望整个OO课程越来越好。
也希望自己能够在代码上精益求精,在追求完美的路上走得更远。