OO第4单元总结&课程总结
本单元架构设计
第一次作业
架构
第一次作业笔者使用适配器模式,建立了MyClass MyOperation MyAttribute MyInterface四个类,将对应的UML元素包装起来,从而使得具有关系的UML元素之间的关系在这四个类中体现(如MyClass类中包含了类中的MyOperation、MyAttribute),便于查找和管理。
而在MyInteraction类中,笔者先将UML元素依次判断类型,并将同类型的UML元素放到同一容器中。然后再对不同类型的UML元素分别处理,从而避免了元素的输入顺序对程序运行结果的影响。如笔者将所有UMLOperation包装成MyOperation后放入以id为key、MyOperation为value的HashMap中,从而在处理UMLParameter时可以根据parentid直接找到参数所属的MyOperation,并将该Parameter放入对应的MyOperation中。
在依据各元素的相互关系建立好图之后,笔者对每个MyClass、MyInterface进行了预处理,将MyInterface所实现的所有Interface全计算好,将MyClass的顶级父类、所有属性等也计算好,从而将大多查询操作的复杂度降为$O(1)$,大大降低了查询操作的总体复杂度。为了避免在预处理阶段重复处理导致TLE,对于每个MyClass MyInteraction预处理时会先处理其继承的父类,在处理完后会打标记,如果需要打了标记的对象,会直接返回,从而避免重复预处理。
由于查询操作输入的要查询的类均为类的name,因此笔者在MyInteraction中还另外建了一个以name为key,ArrayList<MyClass>为value的HashMap,这样在HashMap中查询得到的是一个ArrayList,即可根据ArrayList的size判断是否有多个同名类或者没有这个类。对于其他要通过name来查找的元素也可采用类似的办法。
测试与Bug
本次测试是使用肉眼读代码的白盒测试与写自动评测机的黑盒测试相结合,不过由于评测机的judger不好写,而同一数据的运行结果唯一,因此评测机判断正误的方法从写judger变成了与同学代码的运行结果对拍。
由于本地测试做的较充分,中测难度也较高,因此没有强测出现bug。
第二次作业
架构
本次作业对于类图的部分没有改动,只是增加了对于顺序图和状态图的处理,增加了MyStateMachine MyRegion MyState MyInteraction(上次作业的MyInteraction类这次改名为MyUmlGeneralInteraction,这次作业的MyInteraction是对顺序图中的UMLInteraction进行包装) MyLifeline五个类。
不论是数据结构还是算法还是代码量,此次作业均比上次作业简单,没有什么新的难点,因此不再赘述。
测试与Bug
测试方法与第一次作业相同,同样强测无bug。
第三次作业
架构
这次作业比起上次并没有增加新的查询函数,而是增加了一堆预检查要求,因此对于上次作业的功能性部分并没有太多改动。
由于MyUmlGeneralInteraction类已经快超500行了,因此新建了一个MyStandardPrecheck类,对于模型检查部分,MyUmlGeneralInteraction将相关需求转发给MyStandardPrecheck类,而不再在MyUmlGeneralInteraction中展开。
为了尽可能少影响原有的各个类中的功能,本次作业基本上是采用了在原有类中加成员记录信息的方法来处理,从而基本上只有加代码而没有改代码,尽可能避免了在修改的过程中导致原有功能出错。
如R001检查,笔者在MyClass中新增一个以name为key、ArrayList<id>为value的HashMap,每当向类中增加Attribute或AssocationEnd时就向这个HashMap中添加元素,而在判断R001时遍历HashMap,检查有无size>1的ArrayList即可。
相对而言较为特殊的为R002,笔者采用Tarjan算法检查是否有强连通分量,为此增加了Tarjan和TarjanInfo两个类。
总体而言,笔者认为本次作业的难点主要在于准确理解这些规则,而实现过程则相对简单。
测试与Bug
本次作业依然采用白盒读代码+黑盒评测机的方法进行测试,有效在提交截止前找出了所有bug并修复。最终强测无Bug。
四个单元中架构设计及OO方法理解的演进
第一单元笔者初步体验了面向对象的思想,但是耦合度还是较高,且随着代码量的增长,自己并不能掌控自己的代码,导致后两次作业都出现了一些虽低级但导致了很多失分的bug。
第二单元笔者接触了多线程,对于线程安全、并发性能等问题有了更多的理解,从一个对着wait sleep一头雾水的新手变成了一个能掌控简单多线程程度的入门者。
第三单元通过学习JML,笔者学到了契约式编程,这种架构师提需求、程序员进行实现的分工模式令笔者深刻体会到了契约式编程在多人合作中的重要作用。
第四单元以学习UML的结构为案例入手,感受了UML设计中的OO思想,并且在解析UML的实现中巩固了对于架构设计的练习。
四个单元中测试理解与实践的演进
笔者在第一单元对于测试做的非常不足,既没有耐心在完成后阅读自己的代码,也没有写自动评测,更不知Junit为何物。测试的不足导致了bug不能被及时发现,从而导致了后两次作业的大量失分。
在第二单元,笔者吸取了前一单元的教训,在每次作业后手写评测机,这种测试方法虽然简单粗暴且不能保证覆盖性,但是量变引起质变,在进行大规模测试后基本可以将bug都发现,因此笔者在三次作业的强测中均没有出现Bug,仅仅是第三次作业的互测出现了一个由于鲁棒性不足引起的bug。
第三单元笔者沿用上一单元的测试策略,同时结合了Junit测试,因此三次作业的强测互测均未出现Bug。
第四单元笔者依然沿用这种策略,并且结合了读代码白盒测试,因此三次作业强测依然未出现Bug。
课程收获
通过本学期基于Java的OO学习,笔者对于Java的语法、特性的认识有了从0到1的质变,从一个完全不会Java的新手变成了一个能写出还看得过去的Java程序的入门者。此外笔者接触到了OO这种优秀的设计思想,并将OO的思想尝试运用在其他编程作业中,从而为改善代码可读性、可维护性起到了不少作用。
另外笔者还接触到了IDEA这样的现代IDE。笔者从前的写代码方式一直是用文本编辑器写,再用命令行编译。但是在接触到了IDEA之后,笔者才认识到了原来一款强大的IDE可以好用到这种程度。
改进建议
- bug修复阶段允许0修复提交
在第三单元的互测阶段,笔者发现有的人的代码可能会同时有多种bug,而一组数据就可以触发出所有的bug。假如被hack者有n个Bug,如果hack者比较狠心,完全可以用这种策略hack超过n次,而由于bug修复阶段每次提交必须至少有一个修复才是有效提交,这样的话被hack者只能选择非合并修复导致被计超过n个bug。
笔者认为,bug修复的提交策略可以改为如下:
不论修复情况如何均允许以合并修复提交(如果预览后发现效果不佳,可选择不提交),最终以合并修复提交次数为修复bug数,以最后一次提交后仍未修复的数据点数为未修复bug数。这样,在上述情况下,被hack者完全可以提交n次,尽管前n-1次看起来都没有修复bug,但是最后一次提交后将修复所有bug,而修复bug数将正确的计为n。
- 增加实验反馈
目前的实验环节只有做题,而没有反馈。但是实验的目的是希望同学们学到知识,而不仅仅是检验同学们的学习效果。因此,为了让同学们更好的学到知识,建议增加实验反馈,如在实验结束后给出参考解答。
- 第三单元放弃JML
目前从工业界来看JML并没有得到广泛认可,反而是工具链不完善且年久失修。笔者认为JML不完善的原因与JML自身的不足有很大关系。JML在给出规格的同时以自动化测试为目标,因此只允许使用形式语言而不允许使用自然语言。但是形式语言的表达能力有限,对于稍复杂一些的函数,写出正确的JML规格将变得很复杂,由此带来的时间成本将提高。并且对于阅读者而言可读性也会严重下降甚至导致读错JML、写错代码。大量的时间投入+并未显著增加的代码质量(甚至可能还会因程序员对复杂JML理解出错而有所下降)导致JML看起来并不是一个好的选择。而退而求其次的手动写Junit测试可以用少很多的时间实现大致相同的效果,因此,JML终究难以走入追求效率的工业界。
笔者认为,JML的缺陷在于极端追求自动化而放弃了自然语言的表达,结果导致了规格本身的目的未能很好的达到,本末倒置了。规格本身的目的是什么?笔者认为规格本身的目的是多人合作时对于程序的功能进行沟通,A要求某个函数实现一个特定功能,而不关心内部实现的细节,B根据A写的规格了解到A对于函数功能的要求,于是写出满足功能要求的代码。既然如此,规格应该以高效准确的传达信息为目的。在有的时候,想要使用形式语言准确表达清楚一个意思需要写很多句,而使用自然语言可能一两句话就说完了(如,向集合中添加一个元素)。因此笔者认为不妨放弃自动化的发展方向,专心于追求高效传达信息的目标,不排斥自然语言,在自然语言表述更方便时使用自然语言,在形式语言表达更方便时使用形式语言,从而结合二者的优点,实现规格效率的最大化。
笔者对于第三单元的建议是,不再严格要求使用JML来表达规格,而专注于“使用规格将功能要求说清楚”这个目标。毕竟工具是辅助写好代码的手段,不是写代码的目的,不应为了使用特定工具而背离真正的教学目标。
线上学习oo课程的体会
其实没有太多体会,毕竟不论是在学校还是在家里,都是一样的对着电脑写代码。线上课程的好处是可以同时看好几个班的课堂讨论,从而广泛吸收。