OO第四次作业

一、前言

  随着OO第四次作业的完成,这学期整个OO课程告一段落,在这一学期的课程学习中, 学到了很多,收获了很多,OO这门课程不仅仅是针对我们代码能力的训练,还偏重于我们对所做工作的阶段性总结能力和架构设计上的针对性练习,使得我们在各个方面的能力都有所提升。接下来我将依次对第四单元内容以及课程总体收获感悟进行讲解。

二、第四单元设计

第十三次作业

 1.任务目标:本次作业最终需要实现一个UML类图解析器,可以通过输入各种指令来进行类图有关信息的查询。

 2.架构设计:

  ①考虑到以后任务的可拓展性,我建立了三个类来存储类、接口、属性、方法的相关信息,将需要存储的信息一级一级地分担下去,避免结构上的冗余

  ②由于不保证模型语句的先后顺序,因此我在MyUmlInteraction类的构造函数中选择使用三个for循环对传入的参数进行遍历:

   a.第一重循环找到所有的interface和class的定义语句,通过建立HashMap<String id,ClassMess class>将interface和class用id以及实例化对象建立映射;

   b.在第二重循环找到所有的方法,将方法通过刚才建立的HashMap数据结构找到对应的Class,并且在对应的class中新建一个Method实例化对象,将方法的相关信息存储在该结构中,并且在class内建立HashMap<String id,MethodMess method>将方法用id以及实例化对象建立映射;

   c.在第三重循环中,找到所有的关联association、属性attribute、继承generalization、实现realization、参数parameter,并且将相关信息建立在对应的class、interface、method中。

  ⑥通过这种分级建立信息存储结构的方式,对信息的查询只需要查询到对应的class、interface,然后在其内部进行信息查询的处理,能够很好地简化MyUmlInteraction的结构,使得MyUmlInteraction在完成任务的同时得到功能的简化。  

      

  ⑦为了便于查询以及减少重复查询的时间消耗,我对每一种查询都设置了一个flag位,用来判断该查询是否已经执行过了,如果已经查询过了,那么将直接返回该查询的结果,这样可以避免重复查询的资源损耗。例如查询某个类的顶级父类,我使用递归来查询时,已经遍历过的类的顶级父类实际上已经可以得到了,此时我将把这个结果存储下来,并且将相应的flag位设置为true,这样在下次查询该条路线上某个类的顶级父类时就可以将结果直接返回,从而避免了重复查询。

         

第十四次作业

 1.任务目标:

   ①扩展类图解析器,使得可以支持对UML状态图和顺序图的解析,并可以通过输入相应的指令来进行相关查询。

   ②在所解析的UML模型基础上,按照规定的规则检查模型中是否存在违背规则的情况,并输出相应信息。

 2.架构设计:

   ①由于这一次作业是在上一次作业的基础上进行拓展,因此我是在原架构的基础上,新建了需要的类来对新信息进行存储并且将新信息通过分级存储

   ②同样地,这一次作业也不保证模型语句的先后顺序,因此我仍然沿用了上一次作业的基本架构,使用四重循环对输入的模型语句进行处理,并且增加了对顺序图、状态图的处理操作。

   ③新增了InteractionMess、LifelineMess、StateMachineMess、StateMess来分别对顺序图、顺序图中的LifeLine信息、状态图、状态进行存储,通过这种分级结构,使得整个设计具备较高的可扩展性以及使得各个类的功能明晰,方便阅读以及检查。

   ④这一次作业相对上一次作业新增了检查功能,需要在建立完整个图后对类图进行相应的检查检查:检查模型元素是否有重名、检查是否有循环继承、检查是否有重复实现或继承接口的现象。下面介绍一下如何进行相应检查的:

     a.检查模型元素是否有重名:在每个类中建立一个ArrayList<String>,在构建图的过程中,每当在一个类中加入了属性以及添加了association关系,则将该属性名以及association中的UMLAssociationEnd的名字进行查重操作,如果重复则将该name加入到这个ArrayList<String>中,最后在MyUmlGeneralInteraction中调用每个类的checkR001操作,将所有类的重复name进行汇总存储在HashSet<AttributeClassInformation>中,如若非空,则抛出UmlRule002Exception异常。

     b.检查是否有循环继承:对每一个类(接口),通过递归调用不断访问其父类,并且将已经在这次递归过程中遍历的类存储在一个list中,作为参数传入递归函数,当在递归过程中发现当前类在这个list中,则说明出现了循环继承,将这个类加入到存储需要抛出异常的HashSet<ClassOrInterface>中,若这个HashSet非空,则抛出UmlRule002Exception异常。

     c.检查是否有重复实现或继承接口的现象:一开始我想记录下每条继承/实现的路径,通过比对所有路径来判断是否有重复实现或继承接口的情况,但这种方法的实现难度较大,并且较容易超时。因此选择另一种方法,利用图论中所学的知识,将所有类/接口看作图中的一个结点,找到任意两个结点之间的路径数,如果大于一条路径,则说明这两条路径之间是存在重复实现或继承接口的现象的。具体做法是利用矩阵来做,在此不赘述。

   ⑤这一次建立的类之间的关系如下:

    

 

 

 

 三、总结自己在四个单元中架构设计及OO方法理解的演进

   1.第一单元:本单元的主题是多项式的化简。在第一次作业中完全没有建立起可拓展性的这一概念,因此基本上是按照GuideBook上的要求一一实现的,因此第二次作业就吃了亏,只得完完全全重构,但是第二次作业的架构还是没有很高的可拓展性,因此在第三次作业中又在第二次作业的基础上重构了一部分,这样才磕磕绊绊地完成了第一单元作业。从第一单元的任务中,我学会了架构的设计需要提前规划好以及架构具备高可拓展性的必要和优势,这让我在之后的单元里能够做到在每个单元的第一个任务中就思考好架构的设计以及避免代码的多次重构。

  2.第二单元:本单元的主题是有关多线程电梯接送乘客的问题。由于吸取了第一单元的教训,因此在第一次作业的时候就仔细认真的在思考架构的问题,我采用的方法是控制台ControlCenter对电梯Elevator进行直接控制,并且建立了楼层Floor与乘客Passenger类,用来管理乘客上下电梯。正是由于在第一次作业中就将每个需求功能仔细分析好了,因此在第二三次作业中可以直接加入新的功能,而无需重构,尤其是建立了Floor和Passenger类,能够很好地处理第三次作业中的换乘问题。在这一单元的作业中,我学习了java中的线程问题,以及线程安全的隐患处理,在完成作业的过程中,也出现了线程不安全、错误难复现的问题,这教会了我要好好思考线程的安全问题以及让我体会到了代码架构设计优先考虑完善清晰的好处。

  3.第三单元:本单元的主题是有关JML语言的。说实话,在刚接触到JML的时候很难理解体会到使用规格化语言的好处,但是在接下来的作业完成的过程中慢慢领悟到了JML的优点。本单元的作业主要是针对规格实现代码,由于这次的查询量挺大,因此我选择将所需要查询的信息提前处理好,这样能够很大程度地避免重复计算。一开始我打算使用的是Dijkstra算法,因为Dijkstra算法的复杂度相对较低,但是实际的操作过程中发现Dijkstra算法需要用到的数据结构较多、较复杂,经过测试发现不如直接使用在二维数组的基础上调用Floyd算法,因此最终我选择的是建立在Floyd算法的架构上的设计。这一单元的架构中我没有进行重构,而是在上一次作业的基础上进行扩展。通过这一单元的学习,加深了我对面向对象的理解,尤其是对于规格的运用,避免了我在写方法时不自觉地进入过程性语言思考的范畴,而是转变思路,将方法也归入到面向对象的领域。规格是逻辑严格的,避免了使用自然语言带来的漏洞,提高了代码的可维护性,尤其是有利于大型工程中代码的快速理解与阅读。

  4.第四单元:本单元的主题是需要实现一个UML类图解析器。在本次作业中,我依然是在第一次作业中就尽力把架构设计的完备些,增强架构的可拓展性,避免日后对架构进行重构。这一次作业中我采用的是分层次结构的设计,建立Class、Attribute、Method、Interaction、LifeLine、StateMachine、State类,将所需查询的相关信息都存储在所对应的类中,减轻MyUmlGeneralInteraction的负担,避免数据结构的冗余。在第一次作业中我就使用了这种方法,将类、属性、方法分别建立相应的数据结构,从而避免了第二次作业的重构负担。在本次的单元学习中,加深了我对架构设计的理解,加强了我对架构设计重要性的认识。

  5.总结:从以上对四个单元的架构解析中,可以看出我对架构设计的理解在不断加深,对架构设计重要性的认识也在不断提高,这得益于OO这门课程的设计,OO课程采用了循序渐进的方式一步一步将我们引入面向对象课程的深层次理解,不断加深我们对架构设计的理解,通过实际动手操作以及每个单元的阿反思总结,让我们既增强了代码能力也让我们在总结中认识我们的不足,从而避免在下一次的作业中重新犯下同样的错误。可以说,OO课程的教学方案也是一种架构设计,讲究章法,潜移默化地让我们各方面的能力得到提高。

 

四、总结自己在四个单元中测试理解与实践的演进

   在这四个单元中,课程组培养我们注重的不仅仅是代码书写和架构设计,还有对代码的测试也是一门重头戏。可以说,代码的测试是保证每份代码质量必不可少的一环,如果缺少了代码测试,相信很少有人能够信誓旦旦的保证自己的代码完全没有问题。课程组在第一单元的第一次作业就强调让我们重视代码的测试问题,鼓励我们对代码进行深度测试,尤其是在第三单元,我们学习了Junit对代码进行测试,这样能够基本保证代码的基本功能的可靠性。

  在我的理解中,代码测试是针对任务目标,为了保证代码的正确性以及实现功能的完备性的,因此,在线下测试中需要对代码进行尽可能完备的测试,这样才能更大可能安全通过强测。尤其是对于一些边缘性条件,需要着重测试,一个系统能满足基本功能是最低要求,如果能处理好极端情况才能说这个系统是一个比较好的系统。其中Junit自动测试就能比较好地对边缘性情况进行相应测试,通过Junit测试,可以让其自动完成一些测试项目,从而减少了我们的人工成本,也对系统的正确性、完备性有了更好的保证。


五、总结自己的课程收获
  从总体设计来看,OO课程组的教学内容主要目的在于在提高我们书写代码的能力的同时,培养我们对架构的宏观设计能力,让我们认识到架构具备高可拓展性的重要度,每个单元一次的单元总结也让我们在找到自己代码架构设计的成长时,认识到我们能力的不足,这很大程度上避免了我们日后放下类似的错误。
每个单元的课程总结这是其他课程所没有的,单元总结能够帮助我们梳理本单元学习的内容的同时加深我们对知识点的理解,整理自己在本单元犯下的错误,避免日后重复犯下类似的错误。
  在本课程中我学到了架构设计的重要性,软件体系结构的重要性在于它决定了—个系统的主体结构、宏观特性和具有的基本功能以及特性。正如大型建筑物设计成功的关键在于主体结构,复杂软件设计的成功在于软件系统有宏观层次上结构设计的正确性和合理性。一个良好的架构设计应当具备较强的可拓展性,能够支持后续工作的功能扩展而无需更改整体架构设计,还应当能够将各个部件的功能进行明确的划分,避免数据处理的冗余,做到高内聚低耦合,这样既能够让架构层次间功能明晰,还能使得系统具备较好的可扩展性
  我实践了利用Junit来编写测试,从而可以对代码进行测试,大大简化测试的步骤;还学习了规格的书写以及针对规格填写相应代码,在刚开始学习规格的时候是非常不适应的,以前一直是针对自然语言来完成代码,现在需要现完全理解规格的含义然后再写代码,由于对一些多要求的功能用规格描述的十分复杂,导致了规格理解不准确甚至无法理解的结果,因此有一些不习惯,但是随着训练以及查阅资料,慢慢的能够较正确的理解规格并且完成代码了。学习规格对我们的写代码的意义重大,通过规格不仅能够准确方法的功能目的,还能让别人快速理解代码的意图从而极大的拓展了代码的可读性。
加深了我对面向对象的理解,尤其是对于规格的运用,避免了我在写方法时不自觉地进入过程性语言思考的范畴,而是转变思路,将方法也归入到面向对象的领域。规格是逻辑严格的,避免了使用自然语言带来的漏洞,提高了代码的可维护性,尤其是有利于大型工程中代码的快速理解与阅读。


六、立足于自己的体会给课程提三个具体改进建议

感谢课程组这一个学期以来的辛苦工作与照顾,以下是我个人三个不太成熟的小建议:

1.指导书更新方面:有些作业发布的指导书可能存在歧义或疏漏,出现的问题基本上都会在讨论区通过回答同学的提问从而得到解决,但是这样可能会导致有些同学由于疏忽而错过了讨论区内的一些内容,甚至可能导致程序的正确性收到影响。因此希望课程组能够将补充的问题、细节方面的说明更新在官方指导书中,相信这样也会给同学们带来很大的便利。

 2.课堂内容与实践内容有偏差:感觉课堂所教授的内容与我实际实践的内容有偏差,不能很好地将理论与实践完全地结合起来,因此也较容易导致同学们疏忽理论知识,从而影响理论知识的理解与运用。

3.指导书发布时间有时候较晚:有两三次的指导书发布时间较晚,导致周末大块时间无法用来完成OO作业,也会使得同学的计划出现偏差,希望以后指导书发布尽量不要拖延到太晚。

最后再次感谢课程组这一个学期以来的辛苦付出,希望OO课程越来越好!

 

posted @ 2019-06-23 10:13  C_harlie  阅读(176)  评论(0)    收藏  举报