OO第四单元及课程总结
OO第四单元及课程总结
一、第四单元作业分析
本单元任务是继承课程组下发的接口,完成mdj文件的解析。而要完成这个解析器,就要非常熟悉UML类图构成方式,这也是本单元难点之一。
作业涉及的UML图结构如下:
第一次作业
本次作业只涉及类图,但由于是刚接触UML,因此在理解和建图过程中花费了不少时间。
UML图如下:
第一次作业作业只用关心类图,但本单元作业中类图也是最难的一部分。为了方便管理,本单元我多次使用了适配器模式,比如创建了MyUmlClass
,MyUmlOperation
,MyUmlInterface
等类来管理其对应的类和其下属类,这样的话,可以遵循UML树形结构,从顶向下来建图和查询。
同时,吸取了第三单元过多使用ArrayList的教训,本单元主要使用HashMap和HashSet来管理下属数据,且因为UML图上元素都是以其id(String)为唯一标识,其作为HashMap的键值非常合适,查找的时候也可以很快很方便地找到。
第二次作业
第二次作业增加了对状态图和顺序图的解析。
UML 顺序图
UML 状态图
本次作业增加了状态图和顺序图的解析,总体变化并不大,同时解析类图,状态图,顺序图相对独立,因此可以单独设计,在完成第一单元的基础上,直接拓展即可,很好的遵循了开闭原则,这也是一次很成功的迭代开发的例子。
同时,依然使用了适配器模式来完成,完成方法与第一单元变化不大。
第三次作业
第三次作业架构变化不大,几乎沿用了第二次作业的架构。这是因为相对来说,第二次作业建图两者可以公用,第二次作业已经完成了许多查询方法和完整的架构设计,第三次作业直接基于在第二次作业的架构上增加查询方法即可。
第三次作业有点像上单元,更多的是基于图来查询,同时相较于上单元时间比较宽松,不需要过多关注算法,更多的还是对架构的设计。
二、四个单元中架构设计及OO方法理解的演进
第一单元
经过了Pre,第一单元也不算是入门单元了。但寒假的练习更多的是让你熟悉JAVA如何使用,而非面向对象思想。真正开始接触面向对象是在第一单元,寒假我还在吐槽checkstyle限制太多,一个方法最多能写60行??那就是纯面向过程的思维。进入第一单元以后,慢慢发现对于面向对象来说,尽量要避免冗余,且很多事情都能交给对象做,那还写这么长干什么。这也是我对面向对象的最初步认识。
在进行本单元设计时,感觉自己也变了许多。
第一次作业里,设计的类较少,一个类的任务比较重(由于第一次作业难度不大,也不算太臃肿),几乎没有抽象等,但还是处于一个面向对象和面向过程的交织,有意识的往面向对象上靠,但写方法等又开始函数时思维写下去。同时,也没有太注意迭代,导致只注重完成任务即可,几乎没有考虑到迭代开发,这也导致了第二次作业重构。
第二作业被迫重构,但重构也是有好处的,在又经过了一周的面向对象思想浸透时,再重新审视自己的设计,发现了自己设计架构的缺陷,因此在重新设计时比较注意了。同时,也注意了拓展性,使用了一些继承与抽象,比如将函数项继承于一个类,在使用一个类统一管理,这样减少了冗余,有根据他们相同的特征进行实践。
第三次作业继承了第二次作业,当周实验课介绍了工厂模式,正好使用工厂模式来完成函数项的操作,不过,本单元WF判断我都是用列黑名单方式处理的,应该用一些类来在运行时管理,而不是提前就去判对错。同时,因为本次作业增加了多重嵌套,因此当时在处理时在函数中增加了对高级项的引用,现在细细想来,感觉当时设计其实耦合度较高,有待提高。不过经过这次作业,对面向对象加深了许多认识,编写时,看函数项已经是一个对象了,作为调用者大胆调用函数项的方法对其处理,作为被调用者的函数项保证方法的正确性。
第二单元作业
第二单元设计的也很棒,电梯作业不愧是经典单元。其所涉及的多线程可以让我们感悟很多。
第一次作业,初识多线程,感觉多线程的世界神秘与有趣,但由于一些铺垫,担心轮询,在过程中不停的使用wait
和notifyAll
,导致很容易出现死锁,debug难度也很大。总体来说,第一次作业难度不高,但介绍的多线程经典的生产者消费者模式也很好的帮助了这一单元设计。在本次作业,input作为生产者,将缓冲队列中放入乘客,而电梯线程作为消费者搭载乘客。这些都是对象,管理好自己的数据,做好自己的事,这就是当时我对面向对象的理解。
到了第二次作业,经助教点播,增加了一个调度器,同时本次作业又是多部电梯,调度器非常方便,同时调度器也可以更好地保证线程安全。而调度器思想也加深了我对OOP的理解,很多类(对象)都可以用一个或多个专门的类来进行管理交互,这样许多类不需要直接关联,也很好的体现了“高内聚,低耦合”思想。
第三次作业主要是对多电梯进行了不同的行为规范,这样就可以设置继承或实现接口,将相同的行为抽离出来。本次作业相较于第二次作业主要是调度器更加的复杂,但内核变化不大。同时,这单元也介绍了许多设计原则,比如开闭原则,之前的代码不改就可以拓展新功能,这就需要最初设计的时候考虑很多,留下许多可以拓展的接口,但反省自己的时候,发现自己即使没有重构,也大改了代码,完全没有满足这个原则。这需要我提升对架构的设计。
第三单元
第三单元与前两次单元感觉差别比较大,较少的体现了,更多体现的是契约式变成,其实说实话,这单元并未学习到太多OOP编程的精髓。大致的架构已经固定了,你需要的只是填充方法,或是增加一些类来辅助,但感觉对OOP训练来说还是比较鸡肋。
第一次作业主要就是对JML的理解,但读JML确实也花了大功夫,同时最开始我使用的是ArrayList来管理,因此性能不高。
第二次作业中主要是对第一次作业的拓展,增加了GROUP类,但其实主要是对算法的考察,对性能的考察,因此本单元使用ArrayList查询会显得比较笨拙,所有容器改为Hashmap,查询速度快了许多。
第三次作业是实现一些更复杂的功能,与其说是面向对象,更像是数据结构图单元的作业。在查询最短路时使用了Dijkstra,在查询点双连通时使用了tarjan算法,在找点相连时使用了并查集。
本单元有两个重点,一是JML,二是测试,测试会在测试部分提及。
第四单元
本单元做了一个mdj文件的解析器,比较喜欢本单元的架构,因为本单元设计的架构层次清晰,调用明显,聚合度较小。也符合UML图的树形结构。
本单元的分析上面已经提及。
总结
经过本学期的课程,面向对象思想已经深深印在我脑子里了,有时候写C的时候还在想为什么这个东西就不能给我提供一个方法呢?不过,OOP最重要的还是架构设计,架构不仅要符合现实情况,还要有好的层次设计,行为抽象,抽离共性,避免冗余,就像第四单元作业,根据UML图的结构可以设计一个很好的架构。
同时,对象并未我之前想的那么简单,并非只有底层的实物又或是现实需求的简单对应,比如第二单元作业对象并非只有电梯乘客,还要有更高层来管理这些底层类的调度器,又或是第一次作业的分离器等,而这些又很可能被一些东西管理,层次关系明显,这才是架构,而不是现实生活的简单映射。
三、四个单元中测试理解与实践的演进
测试是面向对象很重要的一环,良好的测试才能保证程序的鲁棒性。
第一单元中,测试使用了python搭载的简单评测机,但终究是随机性测试,而出问题的地方随机性测试虽然可能测试出来,但几率也没想象中那么大,课上也讲了,80%的bug出自于20%的代码甚至更少,因此除了黑盒测试,我还手动构造了样例。因为不可能测试完所有可能的情况,覆盖性测试主要通过自动评测机来完成,而更重要的是手动构建的边界性极端数据,如WF情况,就列举了很多可能出现WF的情况,多重嵌套也列举了很多极易出现错误的样例。最后着重测试逻辑复杂的地方,在本单元就是在嵌套部分着重测试。
第二单元是线程安全,测试难点就是因为线程运行的不稳定性,很难复现bug,因此测试时也主要通过跑大量测试点来进行,因此本单元主要通过自动评测机来进行,通过调整电梯与乘客进入的时间来进行测试,同时为了测试鲁棒性,在自动评测机中,设置乘客进入时间差即是电梯上下层的时间差等,这样可以测试会不会有什么上面问题。
第三单元引入了Junit的使用,主要是对方法测试,而JML单元主要是填充方法,只要方法对了,无需关心架构,就能保证正确性,因此Junit在本单元就可以大发神威,我本单元大量使用了Junit测试,也测出来了不少了bug。同时,课程组推荐的JML工具并未有太大作用,又因为很久没有维护,导致版本冲突严重,配置及其花费时间。
第四单元主要涉及理解,同时因为架构比较清晰,并未搭建评测机。但还是画了许多UML图来进行验证测试。
测试可能和对应任务一样重要,甚至有时候测试花费时间会更大,比如每次搭载评测机也花费了不少精力。但只依靠评测机很难测试完整或者完成覆盖性测试,其很多边界数据还是需要手动构造。
四、课程收获
先是知识的收获吧,无论是JAVA语言的习得,对容器的灵活使用(最开始只会ArrayList),多线程编程中如何保证线程安全与线程同步,阅读JML的契约式编程,还是实现UML解析器,对多态接口的理解等等都是面向对象这门课带给我的。
但是面向对象作为一种思想,承载的远不止这些,上面我所提及更多的就是JAVA编程,但JAVA也只是众多面向对象编程的一种,只是面向对象编程的一个体现。
曾经我认为架构并不是那么重要,只要完成任务即可,即是在计组搭载CPU时也没有怎么关心架构(可能是因为五级流水线架构比较清晰)。但经历了这门课后,每次设计时,我都会很详细的考虑架构,从最小的用什么方式管理数据,到如何提取共性特征行为进行抽象,再到设置一些管理类来统领全局,再到如何预留接口保证拓展性,这些都是我在设计之前会深思熟虑的。同时,在关心一些项目时,会很重要的关心其架构又或是分工等,就连阿里的一些项目等也会专门去看看其架构师设计的是怎么样的,有什么好处。
除了这门课本身知识,这门课也带给我了一些新的体验,有自己做的好的作业,有做的不好的单元,有从大佬学习的研讨课,有实验课,有弱测,强测,互测,bug修复很好的体现了软件开发的许多必要过程。这门课是一门很成熟的精品课。
五、改进建议
- 第一个当然是第三单元,相信很多人都对这个单元有很大意见。OOP编程很重要的是架构设计,但这单元确实体现的很少,取而代之的是更接近面向过程的编写方式。同时,契约式编程更像是最底层的码农做的,感觉一整个单元这个这个有点有失偏颇。而且JML工具链过于老旧,很多都没有维护,实用性也不大,配置方法很多都是这门课程的学长学姐博客里写的,其他的渠道不是很多,配置非常麻烦。
- 第二个就是迭代开发,三次作业是迭代开发,但做每一次作业时几乎不知道下一次作业是什么,只能靠猜,这对预留接口或是迭代开发都是很不友好的。希望课程组能够在以后对以后的作业做一些必要的提示,提示以后会出现什么功能(而不是课上一句话带过去)。
- 关于研讨课。研讨课很多同学喜欢把自己的设计分享,但我认为分享架构即可,细节无需过多提及,但研讨课上很多同学会将细节一个一个讲出来,一来是同学们也很难将其吸收,浪费时间,二来细节在以后设计帮助不是很大,希望以后在审查研讨课内容时这一方面。
六、线上学习体验
这学期是不平凡的一周,线上学习难免会松懈一些,不想写OO代码,不想debug,不想测试的情况很容易出现,但所幸还有这么多同学与我并肩战斗,让我有了向前的动力。
说实话,OO课程由于大部分都是在电脑上完成的,因此就课程本身而言,场地其实没有太多影响(只要有电脑和网就够了),但上课专注力下降确也是一个问题。同时,在网上与同学交流也少了一些,也感觉没有面对面交流直接。这是线上学习的一些缺点吧。
不过线上学习还是有许多优点的,比如视频可以反复看很多次,遇到不懂的反复看加深了印象,而不是对着PPT回想着上课到底讲了什么。
七、结语
经过一个学期的被OO摩擦,感觉自己也成长了一点,这要感谢全体课程组老师、助教们的辛勤付出,感谢每一次互测修评测机bug,感谢JML单元写大段大段JML的学长...,感谢课程组的每一个人。
祝愿OO课程越来越好!