18373599 崔建彬
一、概述
本单元是OO学习的最后一单元,重点学习了UML类图、状态图、时序图相关内容。其中作业要求为:补充函数,完成对UML图的解析。共分三次作业,前两次分别考察对类图的解析和时序图&状态图的解析。最后一次作业是对UML图合法性的检查。整个单元涉及到的算法不多,更多的还是简单的连通图相关的算法(所以又双叒叕用了一大堆dfs &bfs)。这个单元第一次代码量较大,大约一周写了1k行左右,虽然更多的都是简单重复/自动补全的代码,不过写好了第一次后续迭代就轻松了许多。总的来说,这个单元让我初步认识了UML图,通过实验更进一步学会了如何写/修改UML图,也可以说有很大的收获了。
因为本博客还是OO课程的总结,所以我也回顾了一下12次代码作业的内容,除第三次作业有2个强测Bug外,在后续的强测中均未出现bug。可以看出来,整体是有进步的(当然后续题目也简单了)。虽然架构不太行,但是程序正确率还可以。同时,通过这学期课程,真的对面向对象有了初步认知,对Java也有了进一步的了解,对程序检测,迭代开发也有了初步实践经验。总的来说还是受益匪浅的。
二、本单元三次作业架构。
第一次作业,我并没有拆分出MyAttribute、MyOperation等类来实现本地的类的解析。虽然那样做架构更加清晰,更符合面向对象的要求,但我也不知道怎么自己最后一单元写的代码却十分面向过程,说来很惭愧,最后自己真-摸了。(而且导致了我后续迭代也懒得改,继续再上面加功能,无比臃肿。)
第一次单元我用 树 这个数据结构来记录UML图中元素的关系,因为大部分元素都有他的parent,如果仅仅通过for循环遍历,就太啰嗦了。通过树,你可以直接通过父类找子类,或者子类找父类。这个在递归查找时候效果很好。除了树以外,我还通过Node作为图的节点,实现了图。比如关系:Association, 就相当于一个无向图。而继承GENERALIZATION则相当于一个有向图。
但是相关函数实现,我全部让他们挤在MyUmlInteraction类里,这很不面向对象,最好的选择应该是本地拆分出MyAttribute、MyClass等类,让他们共同继承我的Node类(本地UmlClass)。(哎都怪我摸了),下面是我的相关数据分析。
图为 HW13复杂度
图为HW13类图
图为HW13字数
通过HW13类图,也可以看出MyUmlInteraction类极其臃肿,实现了所有的功能,Node是MyUmlElement的意思,里头记录了Association时候的两端分别是谁,又记录了他们的parent/他们的继承父类等等。相当于同时是图和树中的节点。
第二次作业:
第二次作业,需要在上述基础上加上时序图和状态图。实现的都是相对简单的功能(也是助教看我们进烤漆了放了我们一马)。因为实现的功能比较少,有4个函数是一个for循环就可以实现的不必多说,另外两个函数也是一个变相的图搜题,难度较低。总体用的时间较少,所需要加的内容也不多,本来是重构的好机会,但是因为os考试,我还是继续摸了(。但是这次我加了个类MyClass,是用来装我自己实现的用来辅助完成任务的函数。这样MyUmlInteraction就不用太臃肿(就不会超过500行)。下面是相关图示数据。
图为HW14复杂度
图为HW14字数
图为HW14类图
如图所示,从一个类很臃肿变成了两个类(下次一定不这样(如果还有下次
第三次作业:
第三次作业,与前两次作业关系没那么密切,是一些非法性判断。难点在于对题意的理解,对各个函数的理解到不到位,比如Attribute的父类是不是class/ Interface, name是null会不会抛出re等。同时也需要查看异常相关的函数,但总的来说,涉及到的数据结构还是图搜dfs。R001,R005,R006,R007,R008考察相对容易,简单的for循环足以应对,R002 R003 R004涉及到一些图搜的简单算法比如记录有向图回路路径等,但是考虑到cpu时间设置为10s,所以我的On^2算法乱莽似乎问题不大。
架构仍然没有大变,虽然考试完了,但是还是懒得改了,害怕出错,所以仍然无比臃肿,还好这单元没有互测,不然就真的丢人了。下面是我的图示数据
图为HW15复杂度
图为HW15类图
图为HW15字数。
看这几个类,就要承担这么多函数,真是太为难他们了,这一点也不面向对象。没想到最后一周自己架构越写越烂了(其实我是有能力写好的,只是因为考试所以摸了)。希望自己能在下一次写代码时候,能够写出好的架构,能够给大家展现出我oo课真正学到的知识(不是几个dfs,bfs而是真正知道怎么架构)。
三、我在四个单元中架构设计及OO方法理解的演变
看到这个要求确实很感慨。我的架构设计自认为变化是这样的:
第一单元:第一次接触OO,直接被较为复杂的求导给打蒙了。果断面向过程编程,一个类是MainClass作为主函数,一个类盛放所有的求导功能,对怎么拆分完全没有概念。很快到了第二次作业,我的第一次作业架构就完全不适用了,于是我再次发挥面向过程编程思想,果断重构,写了支持第二次作业的两个类,但此时还完全没有意识该怎么区分。到了第三次作业,我又一次被打败了,因为我的第二次架构完全不能够支持我完成第三次作业,这一次我开始认真思考怎么设计才符合要求,真正有了架构的意识,拆分出了项、多项式、元素、加减乘除操作等等,又进行了一次大的重构以后,我开始对面向对象有了概念。这三次重构给我留下了深深的心理阴影,以至于我后续每次作业时,都会先思考好架构,再动笔。
第二单元:这个单元可以说是我架构的巅峰时刻。有了第一次作业的教训,这次我预判了三次作业的要求,仔细思考了架构设计,可以说是四个单元中最令我满意的一次。有了第一单元次次重构的经验,这次我可以说是一直在思考怎么写才兼容性最好,最容易迭代,而且最清晰。我使用了电梯,用户需求,和调度器三个类,分别对输入进行处理,发送给调度器,调度器派任务给电梯,电梯负责运行。果不其然,这样一改后,后续支持多电梯,多楼层就容易多了。虽然还是存在,电梯处理功能和调度器处理功能有交叉的情况,但是总的来说和第一次单元,甚至和后几次单元比,架构都是最清晰的一次。
第三单元和第四单元:这两次一起说,是因为我觉得这两个单元不向前两个单元重点考察设计架构,这两个单元重点考察设计。第三单元主要是对JML的理解,和如何处理函数才能保证程序复杂度合理。第四单元主要是对UML的理解,和对特定需求的实现。这两个单元大部分主体架构,官方包已经帮我们定下了,不过如果愿意的话,还是可以实习非常好的架构的(而不是像我第四单元一样)。说到第四单元就不得不提UML,UML图的元素设计本身就是十分面向对象思想的,也是十分值得我们学习的。
总的来说,可以看出,从第一单元到第四单元,我的程序架构设计确实有了进步,可以说最少能够分清面向对象和面向过程了,没白学。面向对象是什么,是一个很复杂的问题,为了形象化它,我们有许许多多的要求和建议,也有许许多多面向对象的设计模式,工厂模式、单例模式等等。但是在我看来,我在写程序时候始终告诉自己下面这些:高内聚低耦合,每个类做好每个类自己的事,不要去做别人的事。如何拆分才能让他们更具有独立性而不是一锅粥一起煮。这就是我对OO思想最深的印象。
四、我在OO四个单元中,对测试的理解与实践。
四个单元的测试策略我都是这样的:黑盒评测机为主,肉眼扫代码+自己生成的边缘数据为辅助。
说到黑盒测试,我真是要长叹一口气。很多学长说OO不仅学习了java,还学习了大量python和shell,这对那些积极写评测机的同学们来说,自然是这样的。写本地评测机的时间可能和写一个单元作业的时间一样久,甚至更久。(这里实名称赞wpb,他的第二单元多线程(而不是多进程)电梯评测机耗时1天,极其强大,建议明年被录取为oo助教,被拉过去造数据。)黑盒测试在前3个单元都有很好的表现,能够极其高效地核对代码,在大部分时候,评测机挂个1w条就稳了(心里踏实了)。当然有时候也在评测机出bug时候翻车(比如少生成一些重要指令,使得某个函数没有被测试到)。后两个单元整体策略是与同学们对拍,看看行为一致不一致。前2个单元主要是靠评测机实现正确性验证,这个也是最困难的一个地方。但是黑箱测试有个缺点,就是不能保证数据全部覆盖,毕竟数据生成类型是相对比较固定的,灵活性不高,所以写好生成函数极其关键。尤其是在后2个单元,可能评测机起到的作用更小了。
肉眼扫代码:俗称脑测。第一次接触是在大一上写c语言时候,第一次大面积用是在大二上写CO实验时候,那个时候debug基本靠脑测,如果足够认真仔细,还是能够做到效果奇佳的。在今年oo中,我也大量使用了这个方法。当时间不够/刚写完没有数据的时候,我都会把自己最后的希望压在脑测上面。事实上,反复看自己的代码确实有不错的效果,不仅加深了你对自己代码的理解方便你下一次迭代,有时候也能发现评测机不涉及的地方,也是我觉得唯一可能360度全覆盖代码进行测试的方法之一。只要你足够耐心,再加上亿点点运气,就能发现自己的bug。
边缘数据:从第一次研讨课,就有同学们分享我们的代码需要边缘数据来进行测试。毕竟强测卡人的地方都是边缘数据。当然想法很好,这个最难的地方是你想不到哪里会有边缘的地方。随便举几个例子:第一单元求导的时候,((((-x)))) 或者((((-((((-x))))))))这种情况。第二单元时候,让所有人同时在某个楼层上楼。第三单元的时候,等等第三单元似乎没有什么让我印象深刻的数据。第四单元时候,Attribute的父类不是Class/Interface的情况,一个元素的name是null的情况。这些都是边缘数据,理论上如果你考虑的极其全面,确实可以不用写评测机了。而且很多用了大量黑盒测试的同学最后还没有在强测中得到100,很可能就是这种边缘数据没有考虑周全。所以每次写完以后自己手动生成数据也是必不可少的。(有不少同学纯黑盒测试翻车了的)
最后:也是我认为最重要的一点,就是减少自己所写的bug。争取一次写对。当然,天下没有人永远都可以一次写对,但是我们可以尽可能地少写bug,减少给自己的负担。这就要求我们在动手之前考虑清楚,写的时候头脑清醒,意识集中。写完函数,先来一次脑测,过过函数的逻辑是不是和自己想的一样,再来一些简单的针对性数据,测测函数的正确性。这样一套下来,你的作业就基本没有什么bug了。
五、课程收获
其实课程收获的内容在上面也提到了很多。整体来说就是收获很大。学会了面向对象编程思想,学习了Java语言的基础使用,学习了Idea的使用,学习了许多模式如工厂模式,学习了如何读别人的代码,学习了如何快速debug和找别人的bug,学习了如何根据需求生成边缘数据等等,同时,还训练了我们代码能力和迭代能力,可以明显地感觉到自己代码能力比上学期有了较大的进步。
同时,通过这几天刷pyq发现,每个人似乎都对课程很满意,看着一排排AK的强测,大家在符出辛苦的同时体会到了收获的快乐,所以除了上面学习到的内容,还可以说很多同学真的收获到了喜悦,迭代以后AK一片绿的喜悦,Hack到别人bug的喜悦,修复Bug一波带走的喜悦等等。所以说,在精神层面上,OO还是给大家带来了不错的体验(如果给分也能这样就好了)。
总而言之,OO是一门会让我们收获很大的课程,在有难度的同时,又不会让同学们感到害怕,反而每次都能够更有动力地去开始新一轮的学习。
六、三个具体改进建议
1.在讲课中增加一些内容,在这一周课堂中将上一次作业里优秀同学的架构推荐给大家(当然已经有示例代码了)。比如第一单元,第二周课,就可以讲讲第一次作业怎么设计会方便迭代,这样做有什么优点,我觉得既可以帮助那些需要重构的同学,又可以让同学们对架构有更深刻的思考。
2.第一单元难度适当下降,后两个单元难度适当增加。在我看来四个单元难度顺序是:1>2>4>3。虽然1,2是架构需要先讲,但我还是觉得第一单元难度略高不适合放在第一单元,尤其是大家完全没有多少java基础和面向对象编程的基础的时候。3,4单元难度略低,虽然是重点考察设计,但确实有点水了。虽然第四单元进入了烤漆,为了同学们着想,确实不该太难,(不过明年又不是我进入烤漆还要OO)不过确实可以增加难度,尤其是加入架构设计的需求。
3.重视OO预习内容,增加相关内容讲解和研讨。这个建议还是处于我第一单元极差的体验,因为其实预习作业中,就有了对架构的需求,如果能完全消化,第一单元不会很难,但是我当时在寒假容易紧张不起来,导致预习作业没有起到很好的作用,使得我第一单元作业做的异常艰难,一直以为自己要无效作业了。所以我觉得可以在预习内容上加大力度,让同学们重视架构,重视OO。
七、谈一谈线上学习OO的感受
就这学期的线上学习经历而言,我觉得OO是受疫情影响最少的。
考察重头戏作业并没有因为疫情受影响,实验考试和线下效果应该差不多,大家从微信讨论变成了当面讨论。可能听课效果稍差,不过有了讨论环节我觉得效果反而比线下上课效果好,因为线下上课如果没有讨论,真的可能走神就过去了,线上的形式还可以使得我们反复听课,直到真正能回答上来讨论的问题。
研讨课效果也还可以,如果真的认真听了也会有收获,虽然到后期很多同学累了听不进去了,课和研讨课听的都不多,但是我觉得线下上课也不一定能改变这个情况。毕竟线下课程大部分同学听讲情况也不会很认真。
总的来说,我认为线上OO学习和线下并不会有很大的区别,甚至OO学习即使是正常情况,也会是半线上半线下的形式,所以总的感受还是很不错的,体验很好。
八、后记
一转眼一学期就结束了,我相信这学期给同学们留下印象最深刻的一定是OO和OS了。作为计算机学院的核心课程之一,我觉得OO这门课的设计已经可以说是十分出色了,也感谢课程组的改革和投入,将OO从一个知乎上人人吐槽的课程变成了年度好课之一。我曾经看过许多学长的总结博客,也想过自己再写最后一篇博客时候应该说什么,只是没想到这一天来得这么快,也没想到真到了我该写些什么的时候却突然不知道说什么了。