本单元架构设计
第一次作业
第一次作业架构设计比较直接,用一个专门的类存放UML类图,使用 Hashmap 以 UMLCLass
的id为存放每个类下面的属性,操作,关联,继承关系,接口实现等,对接口之间的继承关系也专门设置一个 Hashmap 存放。这么做的好处是直观,存取方便,但需要注意的是每次对 Hashmap
进行 get()
操作时,需要线判断是否 contains()
,如果不判断直接读取到没有存进Hashmap里的 key
会抛出异常。
本次作业的指令除了求类实现的所有接口之外都比较简单,而由于接口支持多继承关系,在搜索类实现的接口时应该类似对图遍历一样进行bfs搜索,这样能保证求得的接口不重不漏。其余的指令只需在对应的 Hashmap 中找到对应存放的 value
即可。
第二次作业
第二次作业的顺序图和状态图的处理与第一次作业类似,对每个顺序图与状态图建立相应的 Hashmap 存放相应的数据,存取操作都与第一次作业没有太多差别。由于本次作业对数据进行了很大程度的简化,在考虑状态转移时不用考虑多种特殊情况,只需对最基本的状态转移做处理即可。对后继状态的搜索同样采取了bfs搜索,能够保证能到达的后续状态不重不漏。
本次作业对循环继承和重复继承的规则检查有些棘手,一开始我本打算顺着继承关系和接口实现关系的 Hashmap
一层一层往上找,但担心这么做的时间复杂度过于高昂,最后将继承关系与接口实现关系转换成了类似于图的邻接矩阵,通过求自己与自己之间是否存在通路来判断是否存在循环继承,求两点之间的路径数是否大于1来判断是否存在重复继承,当UML输入完成之后开始建图,再对每个类和接口依次进行判断即可。
四个单元架构设计及OO方法理解的演进
再次回顾自己前几个单元的作业,其实可以看出许多差别。第一单元开始时刚开始接触面向对象,很多地方其实并没有体现出面向对象的思维方式,更多的是对求导的过程进行建模,甚至存在一个类中一个函数写到底的情况,而没有对表达式和求导规则建立管理层次与抽象层次。当然,在第三次作业中, 由于其复杂性,如果不对表达式建立层次关系将会无从下手,因此我开始尝试对因子、项和表达式进行建模处理,通过继承与接口实现来对不同类型的因子、项和表达式进行管理和抽象,使其能够完成归一化处理。
从这以后我也逐渐开始找到面向对象的思维方法的入口,在设计程序之前开始思考各个部分之间的关系层次,尝试通过封装、继承、多态的方式建立并管理对象之间的层次。
第二单元多线程的设计,要求我们在面向对象的基础上保证线程之间的安全。对输入请求、调度器、电梯,要分别设置线程进行交互,同时保证处理输入类、调度器类与电梯类设计分离,各司其职,对多线程之间的共享对象的操作也应保证准确而不多余,防止出现线程安全问题。本单元第一次作业我就提前考虑到了之后可能增加的需求,将输入处理、调度器、电梯分别进行处理,之后的两次作业也就是增加了对调度算法的调整与多部电梯交互之间的调整,在架构设计方面还算比较满意。
本单元还有比较重要的收获是对高内聚低耦合的理解,高内聚针对的是对象自身,即管理好对象自己的数据,做好自己的事,低耦合针对的是对象对外界的状态和行为,尽量减少对其他对象的依赖,这样出错时不会导致牵一发而动全身。在多线程的程序设计中,保证高内聚低耦合,可以最大程度地降低线程之间交互出错的可能性,这也意味着我们应该按照面向对象的思维合理对各个对象进行建模,合理对对象之间的关系层次进行抽象。
三四单元的架构设计比较相似,这两个单元主要训练的是我们对JML规格和UML图的理解,由于指导书给的指示比较清晰,在设计架构方面并没有多大困难,对不同的模型和数据进行层次化管理,建立数据之间的映射关系即可。这两个单元同时对时间复杂度也提出了要求,因此在存取操作和算法实现时要多留心,防止超时。虽然难度没有前两个单元大,但是不仔细处理仍会出现错误,第三单元第三次作业对换乘、票价、不满意度的计算对于算法方面也是一大挑战。
最后几次作业可以说是对前面作业的整合,当我们再次拿到指导书的时候,第一时间思考的不是给定功能该如何实现,而是应该设计什么类来实现给定功能,类与类之间存在什么层次关系来协助我们完成给定功能,这一学期的OO课就算是没有白上。
四个单元测试理解与实践的演进
OO的课程设置让人深刻体会到了测试的重要性,这是这门课一个成功的地方。通过中测并不代表你的程序有多好,只能说程序实现了最基本的功能,程序的鲁棒性、复杂度、对边界数据的处理在中测中没有任何体现,都需要我们自己构造测试数据甚至自动化生成测试集来帮助我们检验程序设计。
第一单元的测试主要集中于对错误格式的判断上,虽说重心有些放错了地方,在第三次作业的互测中中也及时改了过来,却也让我们对边界数据和特殊情况的处理更加重视,认识到了测试完备性和全面性的重要性。
第二单元的测试主要集中于对程序鲁棒性的测试,在多线程之下程序能否正确运行,线程之间的共享对象是否不出差错,线程之间是否没有出现死锁,等等,都是我们需要考虑的点,也都应该构造足够多的极限数据去进行压力测试。多线程的错误有时候很难复现,本该出错的数据可能运气好跑对了,因此对每组数据也应该进行多次测试,保证测试的覆盖性。
第三单元的测试主要针对时间复杂度,我们需要构造数据量极大的测试数据,并且保证程序能够在给定时间之内成功跑出结果。第三单元和第四单元的测试都可以通过单元测试的方式来完成,这也是提供给我们官方包的好处,我们只需专注于功能实现的正确性即可。这两个单元坦白说我测试得并不充分,有些指令看着很简单,自以为不用测试,但其实在数据量极大的情况下还是有错误。
四个单元的测试侧重点各有不同,从边界数据,到鲁棒性,到复杂度,到单元测试,每个环节都重要,缺一不可,有任何一个方面没有照顾到,测试就不是充分的,程序也不能保证是正确的。手动构造也好,自动化生成也好,测试数据的最终目的就是帮助我们找到程序中可能的漏洞,在测试得不够完备之前,我们都应该把我们的程序当成错误的来处理,程序设计应该有怀疑一切的思想准备。测试同样能够帮助我们在设计的时候提前从用户的角度思考,提高我们的设计思维以及防错意识,这是OO教给我的很重要的一点。
课程收获
最直观的收获自然是学会了一门编程语言,并在作业与实战中不断完善对该Java的理解与对用法的精进。数次作业也体会到了为什么Java是世界上使用范围最广的语言之一,其包含的各种容器和库,构造器,回收,封装,继承,多态等机制,都给这门语言带来了其独有的魅力。
更重要的收获自然在于对面向对象思想的理解与运用,这也是这门课的主题,从最初的面向过程式的思考转变到面向对象式的思考,不仅仅是一句“一切皆对象“就可解决,还需要无数次在架构设计中的斟酌和完成程序后的反思,不断寻找类与类之间的层次关系,思考如何对层次关系进行管理和抽象,满足高内聚低耦合以及SOLID规则,提升程序的稳定性和正确性。经过这学期的训练,在面向对象的设计方面有了长足进步,对于每个类之间的功能分配和层次管理,以及提取抽象对象之间的共性也越来越有心得。
同时,对于测试及其重要性也有了更深刻的认识,一方面是确实吃过测试不充分的亏,一方面是在从中测到强测到互测的过程中逐渐理解了各种测试的意义,开始将测试思维带到设计时考虑,程序的质量也得到了提升。从开始的手动构造测试样例到自动化生成测试数据再到使用JUnit进行单元测试,对程序的测试手段也在一步步完善,使测试得以更全面,覆盖面更广。
还有一些诸如如何写出具有良好风格的代码,如何高效阅读他人代码等的收获,比较微小,但对将来的程序编写也一定会有所帮助。
体会与建议
-
-
上机内容经常是上午才讲过的内容,没有时间消化,上机现场学习效果不一定是最好的。
-
来源:https://www.cnblogs.com/daytona2018/p/11073270.html