OO第四阶段作业总结
一、第四单元架构设计
第一次作业
本次作业的重点在于理清UML类图中类、接口、参数等各元素的基本属性以及相应的层次关系,从而对输入的元素字典表进行解析。需要注意的是,由于各UmlElement的无序性,很可能出现父层次(如类,接口等)还未读入,而子层次(属性,方法,类继承,接口实现等)就已经需要构建的问题。为方便起见,应先扫一遍元素表构造好父层次,再构造子层次。
这次作业中我的架构非常混乱,将所有的HashMap及方法都放在了MyUmlInteraction类中,导致代码冗长且可扩展性非常差,不仅强测翻车时难以修改(论修改499行的类是一种怎样的体验……),而且下一次作业也对第一次实现的内容进行了彻头彻尾的重构,增加了不必要的工作量。在这次作业中我得到的教训是:架构必须摆在实现之前,不要因为可实现就放弃对架构的完善,否则会在工程中给debug和扩展都造成很大的麻烦。
UML类图:
第二次作业
本次作业是增加了顺序图和状态转移图的解析部分,在窥屏讨论区弄清状态图、顺序图所需查询的信息含义后难度并不大,但由于第一次作业作的大死,我用了一整晚进行类信息查询的推翻重构。
架构采用层次化的方法,首先将查询信息拆分为对类图、顺序图、状态图的查询。对于类图,由于类图的查询重点除了类继承、接口实现等关联性信息以外,就是对类内部的属性、操作数量、可见性的查询,我构建了MyUmlClass类,对类内部的属性和方法信息进行构造和查询,实现了层次分离,把类的长度缩减了一半。
在有效性检查方面,对于属性和对端关联不重名,将MyUmlClass类返回出来的属性名和关联中存储的对端名联合起来进行检查(然而因为理解为同一个类只输出一个同名属性而玩出了bug……);对于循环继承和重复继承,采用基本类似的对每一个类进行继承关系搜索的方法,需要注意的是对于已经搜索到的类,再次经过时应当跳过,避免因为下图这种继承关系中有环的情况导致死循环。
UML类图:
二、各单元架构设计总结及OO方法理解的演进
1.架构设计总结
第一单元
对于多项式求导,可将其划分为表达式、项、因子(包括幂函数因子,三角函数因子,表达式因子,常数因子,嵌套因子)3个层次,表达式由项相加构成,项由因子相乘构成。为实现统一的递归拆解求导,我在给每个层次建立类的基础上,让它们实现同一个含求导方法的接口。这一单元的架构,帮助我理解了类的层次划分以及继承、接口等关系的用法。
第二单元
这一单元重点在于多线程中生产者-消费者模型的构建。接收输入请求的线程相当于生产者,执行乘客请求的电梯相当于消费者,它们共同的调度器则相当于托盘。此外,对于多电梯调度算法,还需引入迷宫类对请求进行较好的拆分。多线程中较为重要的是锁的使用,给调度器类的get和put方法上锁,可解决调度器类(即托盘)的线程不安全问题。
第三单元
除官方要求的容器类MyPath和地铁系统类MyRailwaySystem外,将连通与最短路的查询统一用bfs实现,而将最低票价、最少换乘及最小不满意度统一使用Dijkstra算法,新建Graph类进行实现。
第四单元
将查询信息分类为对类图、顺序图、状态转移图的查询,分别构建类进行实现。对于类图,由于存在对类内部属性及操作的查询,可再构建MyUmlClass类存储并反馈类的属性和方法信息,实现层次化。
2.OO方法理解的演进
在四个单元的实践和重构中,我们实现了从主类走天下到层次结构分解的跨越。每个层次可抽象成类,层次中常用的元素可抽象成类,甚至一个可复用的算法也可以抽象成类。类之间通过继承实现子类对父类代码的复用,通过单例构造托盘实现生产者和消费者对容器的存取使用,通过多个方法共性的抽取实现算法的复用。
三、各单元测试理解与实践的演进
1.测试理解
第一单元
手造各种WRONG FORMAT及复杂多项式。
第二单元
针对性的手造可能造成死锁、可能引发请求执行顺序错误的样例。
第三单元
使用Junit进行单元测试;编写数据生成器生成查询指令,可通过等间隔进行add和remove操作、大量执行复杂度较高的查询指令来构造tle样例。
第四单元
画类继承、接口实现较为复杂(如存在圈、多继承等)的类图,构造多个顺序图、状态转移图进行测试。
2.实践演进
在最初的两个单元中,我只是针对自己遇到过的bug以及认为可能出现bug的情形进行针对性的手造样例测试。手造样例打击的精确度较高,但具有思维上的局限性,例如在第三单元中我手造的所有样例都未能发现自己PATH_REMOVE时可能出现空指针错误,但编写自动生成器后一把就WA。因此,精准的手造样例和随机性较高的自动生成样例需要结合起来使用。
四、课程收获
1.面向对象思维
在四个单元作业的摧残中,我逐渐学会将元素、算法及重要的功能抽象成类,学会利用类与接口的层次关系将实现相同方法的类统一起来使用。同时,我在疯狂重构中不断完善自己的代码架构,意识到好的架构应当是每个类与方法尽量简单的,高内聚、低耦合的,不好的架构会给debug以及之后的功能扩展造成巨大的影响。
2.测试方法
第三单元互测时被怼成筛子的心塞硬生生把我从一个佛系手造样例的青年变成写对拍器一通胡砍的狼人(然后对拍器造出的第一个样例就把自己砍了……)。言归正传,我总结出了以下三条重要的测试途径:
1.在研究指导书和编写代码的过程中,记下自己灵光一闪意识到的所有易错点,有针对性地手造样例。
2.编写数据生成器自动生成大量数据进行测试,防止思维定式造成的测试不全面问题。
3.使用Junit进行单元测试(效果类似于写C的时候注释掉前前后后单独测试中间的一段,有利于bug的精确定位),添加断言检查。
五、改进建议
1.最初的几次实验课难度较大,刚接触这门课程的萌新(菜鸡如我QAQ)很容易摸不着头脑and状况百出,建议或者将实验课挪到每单元至少写过一次作业以后开设,或者降低难度and提供与课上任务类似的预习资料。
2.希望优秀代码分享能增加一个简述每个类和方法大概是做什么的说明文档(或者加点注释QAQ),读别人的代码真的很头秃嗷……
3.阅读指导书时,由于各人的理解不同,大家很可能会有一些难以理解的点或者对一些地方产生分歧。尽管讨论区是个好地方,也有老师和助教解答同学们的疑问,但是频繁翻找讨论区还是有一定工作量的。希望能推出一个答疑总结的置顶帖,简要总结有意义的问答,节省同学们研究题意的时间。