OO第四单元&学期总结

OO第四单元&学期总结

一、第四单元作业构架设计

1.第一次作业

第一次作业的任务是实现一个UML类图分析器。虽然程序主干逻辑已经给出,但初次看源码时还是被那套娃一样的文件夹吓了一跳。这次同上一单元一样,新建一个类实现给定的接口即可。主要难点就在于对于各个元素关系的解读。具体思路如下:

  • 对UmlClass、UmlInterface、UmlOperation等类进行进一步封装,记录与其他元素的关系,增添相关查询方法
  • 对于主干类UmlInteraction,在构造方法中对整个类图的逻辑进行构造
    • 首先将元素进行分类,使用HashMap储存id-element对
    • 依次将attribute、operation等添加到相应的类、接口中,并搭建继承和实现等关系
  • 对于查询方法,大部分直接调用封装好的类的相关方法,一些复杂的需要用到BFS等算法

整体结构如下:

方法复杂度(仅显示超标的)如下:

可见大多数方法复杂度都不算高,除了构建类图时添加Element的方法和查询operation数量的方法(这里要考虑return、param等多种情况)

2.第二次作业

第二次作业在上一次的基础上增加了顺序图、状态图,由于新增的查询需求少而又简单,并且有了上一次的经验,第二次作业整体难度不高:

  • 顺序图、状态图的构建沿用了上次的思路,先分类储存元素,然后依次添加元素之间的关系

  • 偷了点懒,没要求查询的元素比如UmlEvent等直接忽略

整体结构如下:

方法复杂度(仅显示超标的)如下:

整体复杂度依然不高,超标的除了上次作业中的两个方法,增加了一个查询message数量的方法。

3.第三次作业

第三次作业增加了模型的有效性检查的要求,由于一些规则的部分情况比较复杂,讨论区中也是问题不断,有不少细节需要注意。由于在运行时间上几乎没有限制,这次用的最多的方法是朴实无华而又有效的DFSBFS,最终也没有出什么差错。

由于这次作业只是增加了有效性检查,程序的整体结构相比上次变化不大,只是多了一个MyUmlStandardPreCheck

二、OO方法理解的演进

1.历次作业构架

第一单元的多项式求导是对于OO的初次认识,由于当时刚刚接触面向对象这一概念,对于这一构架思想的理解还不够深入,导致一开始的构架比较臃肿。主要思路是对于多项式中元素,比如三角函数、幂函数等类进行封装。虽然也使用到了类,但对于面向对象的思想贯彻仍不够彻底,有如下几个缺点:

  • 主类方法过于复杂,融合了解析、检查等功能,应当专门使用一个用于解析输入的类、一个检查合法性的类,使功能更加明确
  • 拓展性不够强:第一单元的作业有局部代码经历了几次痛苦的重构,应该提前想好下次作业的拓展方向,提取一些共性的东西,便于下次迭代设计
  • 有些方法过于“啰嗦”,关于这一点的解决有些棘手,因为有些求导过程确实比较复杂,可以从拆分过程、简化逻辑等角度考虑

第二单元电梯作业的主题是多线程,在这一单元考虑更多的是线程安全、调度算法的问题。一开始的构架中采用生产者-消费者模式,后续的迭代设计中无非是增加了消费者(电梯)的数量,这样的效果还算可以,线程安全得到了保障,可拓展性也是本人比较满意的一点,特别是第二次作业只增加了几十行就满足了新增需求。但是虽然这样设计足够稳健,缺点也是有的,比如第三次作业中由于沿用了之前的调度算法,没有根据换乘的特点改进算法,导致性能方面不够优秀;将调度的功能交给了请求队列,使得电梯与请求队列的交互逻辑不够清晰。这一单元也提醒我应平衡好安全和效率。

第三单元则是对JML的学习,这一单元并没有过于注重整体的构架,毕竟接口已经给定,JML也限定好了各个方法的功能。需要注意的是具体算法的实现。值得一提的是为了提高效率又复习了之前学的数据结构和离散数学,花一门课的时间学三门课:)

到了第四单元,经过了前三个单元的练习,个人对于面向对象的理解也有了些进步,整体的构架在第一部分已经给出,总的来说做好封装,理清每个部分之前的关系即可很快解决问题。

2.对于面向对象思想的理解

    在之前的三个学期编程中,个人接触最多的还是面向过程编程。面向过程最大的好处就是更接近人的办事思维,顾名思义,直接依据处理事物的流程进行代码编写,思想比较直接。但是,在实际的工程任务中,并不是每一次所要完成的逻辑都是直来直去的,这时就需要面向对象来完成,设计合适的类,针对对象来设计自己的程序,可能实现起来更为方便。比如第一单元的多项式求导,如果采用面向过程而不是根据特性进行分类和面向对象,代码过程臃肿不说,整体的构架上也是难上加难。而一旦采用面向对象的思想,进行层次化的设计,各个类之间的关系就很明了,再按照自己的需求对各个类之间进行交互,很容易达到想要的效果。第二单元的多线程更加离不开面向对象,线程之间的交互是重要问题。在第三单元JML的学习中,我更加认识到了面向对象构造的严谨性和规范性,在工程的团队合作中,JML语言就可能起着重要作用,使得不同人编写的代码之间有了交流的可能,也方便了针对性的测试。而经历了第四单元,了解了UML模型,在构建类图的过程中,我对于面向对象编程中的类与类之间的关系有了进一步的认识,同时学习了顺序图、状态图,在今后的设计分析中也能用到这些知识。

    总而言之,面向对象是一种重要的编程思想,很难具体说出它是什么,但在自己动手写代码的时候,这种思想就会一直影响着自己。对于面向过程和面向对象,也不能说两者是完全割裂的,因为在面向对象编程的某个方法实现中,必然有过程需要面对;在面向过程的程序里,也肯定有一个个的对象。我们所需要的是根据任务而选取合适的方法,能利用思想去方便设计、优化设计才是面向对象的最终目的。

三、测试理解与实践演进

    无论是面向对象还是面向过程,测试程序一直是代码编写中重要的一环。在之前的C语言学习和数据结构上机中,代码测试很大程度上是依赖于OJ的,自己很少设计测试样例或者搭建评测机来测试程序,但在将来的科研或者工作中,不可能有现成的OJ来提供测试,因此锻炼自己测试程序的能力就十分重要了。在本学期的OO课程中紧张刺激的圣杯战争互测模式里,我个人对于程序设计的理解也是逐渐深入的。

    在第一单元中,我对于测试程序还处于摸索阶段。在第一次作业中的互测中,想到哪就测到哪,没有按照什么特殊的规律去构造样例,而且测试的方法也很笨,在IDEA中一个一个运行,在八人的房间里,这种方法显然效率极低。第二周的时候,我开始琢磨搭建自动评测机了,最终在第二、三次互测时派上了用场:采取随机生成数据的方法,自动运行测试,效率得到了极大的提高,帮我找到了一些对方的bug。由于数据产生的随机性,这种方法效率高,能检测代码的基本功能,但也正是由于随机性过高,反而对于一些边界情况的bug难以检测。这时候只能靠自己动脑去想一些***钻的样例来单独测试了。

    第二单元中,我同样也采用了自动测评机的形式,自动生成数据,并检测运行结果逻辑。其实这一单元大家在电梯的运行逻辑上出错很少,大部分问题是死锁、线程安全或者超时等等。这时测评机能做到的就很少,我检测出其他人的bug也是看到测评机出现卡壳了才知道有死锁产生。不过有了测评机来检测自己代码的正确性还是有点用处的。

    在第三单元,我就没有搭建自动测评机了,因为在检验结果正确性这一点我没有很好的想法,能想到的也只是用python再重写一遍方法,这样不但白费时间,也会被自己用java写的作业中的逻辑影响,万一java写错了python大概率也写不对。检测代码就靠瞪眼盯代码,对照JML重新梳理逻辑,仔细想有没有漏掉的情况。在互测时,由于有了别人的代码,我就采用对拍的方式,检查结果是否一致。当然,既然这一单元学到了JML,也就学到了一些其他测试方法,比如用jmlunitng等现成工具,缺点是学习、部署工具有点花时间,学会以后用这个对边界情况检测还是可以的。最后就是Junit,在实验中接触这个比较多,我个人认为这个跟之前搭的测评机有类似之处,都是构造样例并检测结果,测试过程比直接在console中输样例然后看结果也更规范,但无论形式怎样,Junit终究只是个测试框架,测试的充分与否还是要看开发者自身。

    第四单元没有互测,样例也需要自己手动去画,于是就偷了点懒只是画几个稍复杂的类图去测了,靠手动测试发现的bug中测也能发现。

    总的来说,我认为对于测试这件事要做的无非两点:1.样例的构造:代码写出来是要被用的,而各个不同的样例就是用不同的方法去用它,要想保证代码的正确性,就要考虑到它会怎样被使用。拿第一单元来说,求导的样例类型是非常多的,测试时应该注意到样例具有覆盖性,随机生成的样例能覆盖一大半,而特殊情况,比如常数、0、超多层嵌套等等就需要自己去想了。如果能对于样例的每种情况都考虑周到,测试就成功了一半。2.对于结果的检验:有了样例,程序出了结果,最后肯定是要检测结果正确与否的,如果把错误的结果当成了正确的,把正确的当成错误的,那样测试反而会取反作用。有时在测试自己代码的时候,尤其是三四单元,我就会陷入矛盾之中:我不知道自己的结果是不是对的,所以要测试。但是测试结果需要用正确的结果去对比,如果我知道怎样是正确的,那还测试干啥。其实后来想想,这种矛盾的来源就在于:需要一个绝对正确来作为标杆。这个标杆不能是自己写出来的代码,要能保证正确性。举例来说,第一单元用到的标杆就是python的sympy库,相比自己来说,官方的库出错几率几乎为0,就可以用这个作为标准与自己去比。第二单元,我搭建的测评机评判逻辑也是按照指导书对于结果的规定一条条写的。而三、四单元,缺少了这个标杆(后来知道python有networkx这个库可以用),导致如果不用工具的话测试难以展开。因此,有了能检验结果正确性的东西,测试就好进行很多。回想之前用过的测试方法,无论是用python写测评机,还是用junit,根本上都是依照这两个逻辑。

四、课程收获

这一学期OO上完了,收获肯定是不少的,毕竟最花时间的一门课。主要有以下几点吧

  • 面向对象的编程思想:正如课程名所说,这门课是教会我们面向对象设计与构造,经历了四个单元的洗礼,编程的思维多少还是受到了一些影响。第一单元的层次化设计,教会了我写代码前要先分析程序的内部结构,思考怎样写可拓展性强,类和类之间应该是怎样的关系、如何降低程序耦合度,以及一些设计模式的使用等等。第二单元的电梯则是自己第一次接触多线程这个玩意,程序就变得"动态"起来,这单元更多是学习一项之前不会的新知识,学会了怎么设计线程安全的程序,其中一些内容也帮助了OS的学习。第三单元的JML则是教会了我编写程序的规范性,让我知道还有一种东西可以让代码之间的沟通变成可能,让测试变得有针对性。第四单元则让我了解了类图、顺序图、状态图等的使用,编程的时候也可以用这些图来做规范。面向对象不是具体的方法,而是一种思维,虽然上课学到的东西可能是抽象的,但一次次作业和实验对思想的潜移默化造成了最后质的飞跃。
  • 工程开发能力:之前的C语言课编写的程序多半用完即废,行数也很少过百,同时也存在一main到底,命名abc的小毛病。这学期的OO作业则是让我们尝试开发一个小型的工程。首先,代码风格就要进行规范,从一开始checkstyle经常报错,到后来写完第一遍check几乎没错,代码风格已经逐渐成形。还有就是编写代码时目光也放长远了,开始想着怎么方便后续拓展,怎么降低耦合度,用什么算法去优化。不得不说,虽然一次次的作业很累人,但最终写下来还是很有成就感的。
  • 代码测试能力:上面已经详细说明
  • 其他零碎的知识和能力:这一点并非是课程直接教会我的,而是个人在本课程的摸索过程中学到的一些零碎的东西,比如配置环境变量、安装一些软件、工具的方法(尤其是第三单元)、java语法的掌握、搭测评机时对于python使用更加熟练等等。在学习OO时偶尔会遇到一些难以解决的问题,无论是用StackOverflow还是CSDN之类的什么方法,寻找解决办法的过程就是对自身能力的一种提升。

五、一点改进建议

在上这门课之前我就已经对OO课程略有耳闻,知乎上对于2019年以前的OO制度的评价大多是负面的,但经历了这一学期的OO学习,发现课程制度和安排已经算是比较合理的了,一定要提改进建议的话,有以下几点:

  • 每次实验结束后公布参考答案。个人感觉这学期有几次实验题出得有点迷,不知道到底要干什么,做完以后也不知道自己做了什么。而且周围不少同学也是这种感觉。建议下一届OO课实验后可以公布答案,方便同学检查
  • 第三单元作业的代码发布前多检查下,记得在第三单元JML第一次作业的时候,指导书和给定代码前前后后修改了6次,还要重新pull然后改代码,体验不太好
  • 第三单元作业可以更紧贴课程一些,我自己在做第三单元作业,尤其是最后一次时,感觉完全是在学离散和算法,重心都放在了算法效率上,学到的JML相关的知识实在有限。虽然交叉一些知识是好的,但也不能脱离作业的初衷,还是紧贴课程好一些

六、线上OO学习的体会

这一学期感受了OO全线上教学,总的来说体验还是很不错的,虽然花的时间最多,但收获也很多。线上学习最大的好处就是灵活性,采用录播课的方式不仅能自己根据实际情况安排时间,还能回放复习,这一点非常方便。讨论课的氛围也很不错,虽然不是面对面的交流,但最终效果也很好。

最后,衷心感谢OO课程组的每一位老师和助教们,为我们带来了这次难忘的学习经历。

posted @ 2020-06-19 14:01  xcw1010  阅读(161)  评论(0编辑  收藏  举报