OO第四单元总结
一.第四单元三次作业结构设计
1.第一次作业
第一次作业的架构设计较于第二次的和第三次的显得格外重要。因为在第一次实验的架构确定后,第二三次的实验就简单多了。由于本单元的测试用例都是首先给出该Uml图有关的信息,然后输入一些查询指令,所以在查询指令输入前各种状态就已经完全确定了。所以为了节约时间,我UmlClass,UmlInteraction,UmlInterface等类来存储查询指令可能查询的信息。这样查询指令本身的复杂度直接被降为O(1),即只需要直接查询即可。我采用init类来根据输入的信息算出各种状态并存入UmlClass,UmlInteraction,UmlInterface等类的属性中,这样就不需要反复计算了。
2.第二次作业
第二次作业加入了顺序图和状态图指令的检查,我重写了UmlClass,UmlInteraction,UmlInterface,UmlLifeline,UmlStateMachine等类来存储有关信息。在第二次作业新添加的指令中,相对比较有难度的是给定状态机模型和其中的一个状态,有多少个不同的后继状态,我才用的方法是从要求后继状态数目的状态出发,使用dfs算法判断有向图中从此顶点出发能够达到顶点的数目即可。
3.第三次作业
第三次作业加入了对类图有效性的判断。我觉得本次作业较于前2次作业并不复杂。我重写了UmlClass,UmlInteraction,UmlInterface,UmlLifeline,UmlStateMachine等类来存储有关信息。在进行有效性的检验时,我主要把检验的方式都通过图论的方式,来解决。比如判断R002是否有循环继承即判断有向图是否存在环路,这只需要通过dfs方法遍历判断从每个类和接口出发是否存在一条路径能够回到该类或接口。如果可以则说明图中存在环路,应把环路上的所有类和接口都放入集合中,就可以不重不漏地输出。判断R003时任何一个类或接口不能重复继承另外一个类或接口,即从类和接口出发到所有它继承的类和接口都只存在一条路径,这也可以通过dfs比较简单地判断。
二.架构设计与OO方法理解的演进
1.第一单元
在写第一单元的作业时,我对架构的设计还很不熟练也很不完善,主要以保证正确性为主,对架构的考量,对类与方法的恰当分配,以及对设计模式的应用都还很不充分。这导致了我作业的可扩展性很差,基本写第二次作业与第三次作业时都进行了重构,导致作业的工作量非常大。然而在研讨课以及查看模范代码后,我对面向对象思想的美妙有了初步的体会。
2.第二单元
第二单元主要以多线程为主,遭遇的bug也比第一单元复杂而且难发现的多。最重要的是,多线程的bug复现起来比较困难。
第二单元我较于第一单元的进步主要为代码复用的比例明显升高,基本不需要重构了,这与第一单元我的惨痛教训密不可分。第二单元中我采用了一个scheduler类来进行所有电梯有关的等待的人的分配。第二单元我采取了策略模式,这样导致当我为了提高性能改变电梯运行的算法时就非常方便了。然而在第二次作业中我发现这样会导致scheduler类格外冗长。所以我在第三次作业中有一个总scheduler,也为每部电梯分配了一个单独的scheduler,拆解了第二次作业中冗长繁琐的“上帝类”。
3.第三单元
第三单元多线程内容引入了jml。由于助教已经详细地给出了每个类与方法的用途,所以我们还需要按照助教的jml实现即可,难度较前2个单元低了很多。同时经过查看助教对比较复杂的网络查询架构的设计也对我们日后自己设计架构时有所帮助。助教的设计让我更好地理解了类与方法功能恰当分配的OO方法。
所以第三单元的难度主要集中在算法方面,如引入缓存来减少二次尤其是三次循环的出现,以及使用堆优化等方法来尽可能降低时间复杂度。
4.第四单元
第四单元的架构设计已经在本博客的第一部分中叙述地比较清楚了,在此不再赘述。关于OO方法,我在本单元作业中主要应用了缓存的思想。虽然用到的方法等往往不会特别复杂,然而代码量却出乎意料的大。这也就要求我采用合适的方式来分割不同类的职责,有效避免“上帝类”的出现。比如第三次作业中我在主类终止几乎只保留查询相关的内容,尽可能地将计算不同状态的代码分散到有关的各个类中。我用init类来计算模型中查询指令查询各个状态。我觉得最大的难点是缓存内容的次序特别重要。我的多个bug都与缓存内容的次序有关。比如计算某个缓存内容时可能需要依赖其他的缓存内容,如果这时其他的缓存内容尚未计算出来,或尚未更新,则会出现严重的问题。
三.测试理解与实践的演进
1.第一单元
我没有采用自动化测试,全靠手动构造并输入样例进行。虽然有样例更具有针对性,较于随机生成的自动化测试数据能更好地发现bug等特点,然而也要具有测试麻烦而且没有进行覆盖性测试的缺点,导致第三次作业里出现了非常严重的问题。
2.第二单元
我采用了自动化测试的方法。然而第二单元的自动化测试我觉得是前三个单元里最难写的,因为正确答案并不唯一,只能根据作业里给出的判断正确答案的标准进行判断,而不能采取对拍等方式。这单元自动化测试的书写让我收获颇丰,不仅学会了一些python的知识,还学会了如何命令行运行并输出结果,以及如何使用python程序来打jar包。虽然intellij也可以打jar包,然而如果在写作业的过程中多次修改则手动打jar包会非常繁琐,而采用程序来自动打jar包就非常方便。
多线程有一大难点是bug的复现非常困难。所以我在评测机的编写时就会顺便输出一些我关系的状态的值。这样如果评测机报错我就不需要进行bug 的复现,只需要直接查看打印出的某个时刻我关系的状态量的值即可,就可以相对非常轻松地定位到对应bug。
3.第三单元
我尝试了多种测试方法并对各种测试方法有了较为深入的理解。下来将重点讲一下第三单元的测试方法:
主要的debug方法我觉得主要有以下几种:
- junit
- 找到一份大佬代码,随机生成测试数据,然后对拍
- 使用python专门的图论库networkx
- jmlunitng,solver等
几种方法中我觉得相对最不靠谱的是junit。junit虽然在迭代开发TDD时确实非常方便,但是junit的测试样例是我们自己编写的 ,据我所知,这个单元的很多bug都是因为没有看清规格导致的,而junit显然无法解决这样的问题。
和大佬代码对拍这个方法我个人比较推崇,首先我自己就是这么做的,其次确实非常有效,我帮别人和自己都发现了bug。最关键的是,本单元的对拍很好写,以为结果的判定是唯一的,不像电梯单元那样有很多个正确答案导致对拍写起来就会比较复杂。
networkx库这个方法我觉得也很不错,这个图论库不得不说非常强大,很多作业中最复杂的几个函数胡只用python 的一条语句就可以很好地检验了。
jmlunitng,solver等我觉得配置繁杂而且基本起不到debug的功效,很不推荐。
4.第四单元
由于图的特殊性难以自动生成数据(反正我是确实不会),所以我采用了手动构造复杂特殊图形的方式来检验代码的正确性,也取得了比较好的效果。
四.课程收获
1.学习了有关java的相关基础知识以及主要应用。在学习本课程之前对java一无所知到现在可以一周写并debug上千行代码。
2.渐渐地开始掌握面向对象的思想,渐渐学会让自己的代码符合规范,一目了然,易于阅读。
而且学会了合适地分割类与方法等。不能让方法太长会严重影响可读性,也不能允许“上帝类”和“傻瓜类”等的出现,这在第二章电梯的控制运行和第四章中体现的尤其明显。第四章如果类的分配不合理就比较容易出现某个类迅速达到500行代码的情况。
3.学习了很多种非常有用的设计模式,如工厂模式,观察者模式等,非常有用。
4.对java的工具有了比较深入的了解,如intellij的使用,以及intellij各种有用插件的了解与使用等。
5.学会了对多种测试方法的应用,包括同学之间代码对拍,使用junit做回归测试,使用python各种库来检验自己代码的正确性,尤其是第三单元第三次作业的networkx库,java中挺难实现的方法都可以用python方便地一行实现。
6.练就了临危不惧的心理素质。我本人比较喜欢赶ddl,有时ddl已经临近却还有不少bug,这时就需要较好的心理素质来稳定情绪,短时间内找出bug。
五.具体改进建议
1.关于实验
本学期我们是不知道实验的结果的。虽然杨老师每次实验前都会公布上一次实验整体的情况,然而我们却根本不知道自己的成绩,也不知道自己到底通过了哪些数据点,根本没有修正自己错误的机会。我觉得可以公布每次的成绩并将通过了哪些数据点发给同学们,还可以发一份助教们做的标准答案。
也许有人会说:“计组和操作系统的实验不也不公布数据点吗”。我想说的是在同学们的学习过程中可以明显觉察出这几门课对实验课定位的巨大不同。无论是计组还是操作系统实验采取闯关制,核心目的是检验同学们是否通过对应lab。而且由于如果不通过再次参加时的数据点或许和第一次有很多是一样的所以显然不能公布。然而面向对象的实验并非闯关制,其核心目的是让同学们更好地掌握刚刚学习的内容来为作业打下基础。按我的理解这也是为什么每次实验都在周三晚上,刚刚学完新知识后进行的原因。
2.关于作业
我个人确实觉得作业的设置主要有以下不合理的地方:
1.我觉得章节的顺序应该为第三章,第一章,第二章,第四章,理由如下:
- 难度上我觉得按照章节作业难度排列的顺序是:第二章 > 第四章 > 第一章 > 第三章。
- 而按照知识的递进层次,第一章主要让我们使用了各种容器,以及初步了解了一些设计模式,是第二章第四章的基础,所以必须排得比较靠前
- 第三章学习了jml,然而与前2章的内容却并非递进关系,所以可以放在前两章内容前。
- 我觉得在写第一章作业时设计类与方法时还不太熟练与合理,而第三单元助教给出了每个方法的jml只需要实现,所以我觉得对同学们来说也是一种学习类与方法的设计的一个好机会。
2.我觉得第四章第一次作业的一些内容应放在第二次作业。或许是我的个人感受,但我确实觉得第四章第一次作业的难度远大于第一二三章作业的难度,而且我甚至觉得第四章第一次作业要做到bug-free的难度比第二次第三次作业新增的内容的难度还大不少。
3.我觉得有的弱测测试点有点强,或许放在中测更为合适。
3.关于讨论课
1.关于讨论课我觉得以后可以不采取所有都得同样的分数,而采取基准分 + 评价分。基准分即只要参加了讨论课就能获得的分数,评价分即按照研讨课的主讲人是否足够认真,讲的内容是否具有足够的原创性等进行评分。这样既鼓励了同学们参加研讨课,也避免了研讨课的主讲人“划水”,“掐烂分”等不太好的现象。
2.我觉得以后研讨课不仅可以举办每个班级之内的,甚至还可以抽出额外时间举办整个大班在一起的研讨课,在大班研讨课上让小班内共认的特别突出的内容在大班研讨课上再讲一遍。研讨课不同班级主讲人的内容并不一样,我建议有的讲的特别好的内容可以在大班内共同分享。
4.关于预习课程
我觉得助教如果方便的话是否可以考虑在寒假初期就发布预习课程题目?这样同学们支配作业的自由度更大。比如我在这个寒假中由于2月14到2月18日有建模美赛所以做作业的时间确实比较紧张,当助教在约2月10日发布题目后我由于还要准备并参加美赛等就耽误了不少时间导致做作业时间收到比较严重的压缩,然而如果能在寒假初期就布置作业的话,就不容易出现类似的情况了。
六.线上学习体会
由于疫情原因,本学期只能线上学习。在本学期刚刚开始时,原本对本学期OO课程能否有效展开还心存疑惑,然而通过一学期的学习后很好地打消了我的疑惑。
课上学习确实有一些弊端,比如只能听到老师的声音不能看到老师的肢体动作等,也不能看到老师的板书。而且如果在教室里上课老师可以通过提问来确定同学们对这部分内容是否掌握充分,然后可以对讲课进度和速度做一定的调整,然而这些在线上学习中却不太可能实现了。
然而线上学习也有一些好处:
1.网课由于是录制的形式,所以学生听课时自由度很高。如果又不懂或遗忘的地方可以反复听录制视频,如果觉得老师的语速不太合适自己也可以根据自己的喜好调节老师的语速。而且老师一般都会提前发布网课,所以可以自由选择任意课前的空余时间进行听课,自由度更高。
2.据我观察,我觉得线上的讨论与研讨课比教室里的更热闹了。有的同学如果在教室里或许相对比较腼腆,或许不太愿意分享自己的想法,然而在线上由于不需要直面同学们和老师的目光压力小了很多,也就相对畅所欲言了。我觉得微信群里的讨论的热闹程度远远大于在教室里的热闹程度。
在这次疫情之后,同学们肯定会回到教室里进行相关课程。我建议给下一届同学上课时,可以既在教室里完成正常的教学活动,同时可以把这学期录制的视频发给下一届的同学有利于他们课下进行学习。
虽然一学期的OO课程可以说是非常痛苦的,然而却是得到了特别大的收获,最后再次感谢一学期以来各位老师和助教对课程的辛苦付出!