BUAA_OO第四单元作业总结——UML

BUAA_OO第四单元作业总结——UML

单元任务

       本单元的任务是根据标准输入解析UML类图、状态图、顺序图,完成相关查询指令的实现。其中第一次作业是实现类图的解析以及相关查询指令,第二次作业在第一次作业的基础上添加了状态图和顺序图的解析和相关查询指令,同时在完成类图解析之后,需要检查类图的有效性,即是否存在违反规则的情况。

一、本单元两次作业的架构设计

1. 第一次作业架构

       第一次作业任务为解析UML类图,本次作业的难点在于建立类图的树形结构,将导出的每一项存在对应的位置。下图为第一次作业的类图。

       GjxUmlInteraction类主要负责构建类图的树形结构,在解析每一项时,将其存储到类图结构中的对应位置。解析到UML_CLASS类型,将其添加到classList以及classId中,解析到UML_INTERFACE类型,将其添加到interId中。其中classId和interId均为HashMap结构,key为Id,value分别为GjxClass类和GjxInterface类。因为像Attribute和Operation等项需要通过parentId来寻找其对应的Class,通过HashMap结构可以通过Id直接获取到对应的对象,避免遍历带来时间浪费。

       ClassName和ClassAll是为了检查是否存在同名类而存储的HashMap,ClassName的key是Class的name,ClassAll的key是GjxClass类。当存在同名类时,因为HashMap结构的key是唯一的,因此ClassName中只存在一个该名称的类,而ClassAll的key因为构造了两个GjxClass类,因此两者是不同的,ClassAll中存在多个该名称的类。当通过Class的名字进行查询时,通过ClassName的HashMap可以快速获得该名称对应的GjxClass,从ClassAll中移除该类之后,如果value还包含该名称说明存在同名类。

       因为UML类图解析的结果是不确定的,当解析出继承关系、关联关系和实现关系时,可能另一端尚未解析到,因此,先将继承、关联和实现分别存在链表中,等解析完全部信息之后,再将其添加到对应的类和接口的属性中。

       GjxClass类重新实现了Class类型,将相关的信息存储到其中,包含了该类所有的属性(Attribute)、所有的操作(Operation)、所有的关联(Association)、所有的实现接口以及其父类。GjxClass还定义了关于类的各种操作,因为可能存在继承关系,在进行查询时,可能需要对父类也进行查询,因此许多操作例如getArrtNum()需要进行递归计算结果,同时对于这种需要进行递归的方法,我分别设置了一个属性用于标记是否进行过计算以及存储计算的结果,当下次再次进行相关查询时,如果已经进行过计算则直接返回结果。

       GjxInterface类重新实现了Interface类型,因为本次作业中没有直接对接口的查询指令,所以其需要存储的只有继承的父类接口。

       GjxAssociation类重新实现了Association类型,其中存储了该关联的UML_ASSOCIATION和对应的两个UML_ASSOCIATIONEND。

       GjxOperation类重新实现了Operation类型,其中存储了该操作的所有参数,在添加参数的同时统计各种参数的个数,便于之后进行查询。

2. 第二次作业架构

       本次作业的主要内容是解析UML状态图、顺序图以及判断类图的有效性。下图为第二次作业类图。

       本次作业中类图解析部分与上次基本相同,在MyUmlGeneralInteraction类的构造方法中判断Element_TYPE,如果是属于类图的部分,交付MyClassModelInteraction进行构造,其余部分与上次作业完全相同。

       本次添加的状态图通过MyUmlGeneralInteraction类和MyStateMachine类进行构造解析。在构造的过程中,因为State、Pseudostate、Finalstate和Transition的parentId不是StateMachine的Id而是Region的Id,在构造时是根据StateMachine进行构造的,因此需要定义一个HashMap来存储Region的Id和MyStateMachine的映射关系,便于查找。

       MyStateMachine类中包含所有的State、Pseudostate、Finalstate、所有的Transition,以及对应的查询实现方法。

       MyState类中包含其所有的直接后继状态、通过计算得出的所有后继状态以及对应的查询实现方法。因为计算后继状态时需要进行递归,所以我同样设计了一个Flag来标志是否已经计算过后继状态,并将结果进行存储。后继状态中的FinalState与普通的状态是不同的类型,因此需要单独进行存储。

       状态图解析的难点是后继状态的计算,因为需要递归的原因,非常容易死循环。我采用的算法是遍历直接后继的每个状态,如果直接后继为自己,则将自己加入后继状态集合中,如果不是自己且不在后继状态集合中,则对该状态求后继状态,将结果加入自己的后继状态集合中。当出现过直接对这个状态求后继状态数量的指令时,Flag属性为true,如果Flag为false,需要在进行计算之前清空后继状态集合,不然可能会出现少算状态的情况。

       顺序图通过MyCollaboration类进行构造解析,其中包括所有的LifeLIne和Message。MyLifeLine类中包括所有的Incoming message。查询LifeLine数量时,直接返回lifelines的size(),查询message数量时,直接返回messages的size(),查询LifeLine的incoming数量时,直接返回incomings的size()。

       本次作业的另一个难点在于类图规则的检查。对于UML002规则检查,我采用的算法是遍历每个类,在每个类中,先查询有没有同名属性,再遍历查询关联的另一端有没有和属性同名的情况,最后遍历关联的另一端有没有同名的情况。

       对于UML008规则检查,我采用的是先构造一个集合存储循环继承的类或接口,之后先遍历所有的类,如果该类不在结果集合中,则以该类为起点递归查询是否存在循环继承,如果最后会回到该类说明存在循环继承,将该路径上的所有类添加到集合中,作为结果返回,为了避免递归查询的过程中出现循环继承导致死循环的情况,还要构造一个集合记录递归过程中经过的类,如果出现重复,说明已经出现循环,应该查询下一个父类。之后遍历所有的接口,算法与类算法类似。

       对于UML009规则检查,主要是查询实现的接口之间是否存在交集。为了避免重复计算,我也构造了一个集合存储结果,如果发现父类或者实现的接口再改集合中,那么该类或接口必然存在重复实现,将其加入结果集合中。

二、四个单元中架构设计及OO方法理解的演进

1. 第一单元

       第一单元的任务主要是从面向过程的思想到面向对象的思想的转变,同时熟悉java语言编程。当时我还没有了解到架构的重要性,因此第一次作业只是完成指导书上的要求,没有去考虑架构的可扩展性,在完成第二次作业时,进行了大量的重构。在编写第二次作业时,因为吸取了第一次作业的教训,留出了一些可进行扩展的部分,因此第三次作业只进行了少量部分的重构。

       在编写第一次作业时,还不是很理解面向对象的方法,还是一个类,在类的内部去调用各种方法。到了第二次作业,才开始有了一些理解,开始将多项式和项封装为2个类,并且将相关的操作封装到对应的类中。到了第三次作业,因为类的数量开始变多了,我才体会到面向对象方法的好处,类和类之间相互独立使得debug变得方便了许多。虽然有一些理解了OO方法,但当时对于接口以及继承的使用不是很理解,在实现的时候没有使用最简洁的方法。

2. 第二单元

       在这个单元中,我首次接触了多线程程序,学习了多线程程序的编写,多线程也是面向对象思想中重要的一个部分,只有明白了线程之间是如何进行协作的,才能实现多线程,其中的重点是保证线程安全,确保共享对象是安全的,即同时只有一个线程访问共享对象。

       有了第一单元的练习,这个单元在实现的过程中没有进行过大量的重构,每次作业都是在上次的基础上增加修改。这一单元设计的重点在于将控制和电梯的运行分开,电梯类实现电梯的开门、关门、上下行、上下乘客等等操作,控制类实现请求的调度,从请求列表中选择合适的请求分配给电梯,总体分为四个模块,第一个是主线程,控制整体程序执行,第二个是输入线程,从标准输入中读取请求,并将请求添加到请求队列中,第三个是调度器即请求队列,负责将请求调度给电梯,第四个是电梯线程,模拟电梯运行。第一次作业和第二次作业的主要差别为调度算法的差别,第三次作业在第二次作业的基础上增加了电梯数量以及电梯自己的内部调度算法。

       在这个单元,我感受到了模块化对于程序可扩展性的重要性,也体会到了一个好的架构能为后续扩展带来的便利,对于面向对象的方法也有了更深的理解,明白了对象和类的区别,学习了多线程编程的方法。

3. 第三单元

       第三单元的主题是JML——Java建模语言。大型项目往往需要一个团队来一起完成,JML为团队合作提供了便利,使用者只用关心输入的要求和输出的结果,不必考虑实现的逻辑,而实现者则可以只根据输入和输出来实现方法,不必考虑其他方法的实现。

       这个单元是通过层层递进的方式来实现一个地铁系统,最主要的问题还是在于图结构的构造、中间结果的保存以及图的查询算法。第一次作业只是单纯地存储路径进行查询,并不需要形成图结构。而第二次作业当我使用矩阵进行存储的时候,在强测中就出现的超时的情况,原因是遍历次数太多。因此在第三次作业中我进行了重构,修改了自己的存储方式,将中间结果保存起来,便于之后的查询。

       这个单元主要是接触了JML语言,体会到了正确的规格为编程带来的好处,从逻辑的角度去思考一个方法带来的变化,而不是从算法的角度去思考如何去实现这个操作。

4. 第四单元

       第四单元的主题是UML,一共是两次作业,任务是解析UML的类图、状态图、顺序图以及检查类图是否符合UML规则。

       这个单元的代码编写并不困难,只要明确了具体要求,实现起来并不困难,主要的目的在于让我们在程序中将类图、状态图和顺序图重新构造出来。这种构造使得我们对于UML的三种图有了更深的理解,同时也加深了我对于面向对象思想的理解,类图体现出了类之间的关联、继承关系,体现出了与接口之间的实现关系,状态图体现出了一个类可以出现几种状态,而顺序图则体现出对象之间的交互和协作。

       程序架构方面基本是按照类图的结构进行搭建的,第二次作业在其基础上添加了状态图和顺序图以及规则检查。

三、四个单元中测试理解与实践的演进

       测试对于一个程序来说是不可或缺的部分,没有人能够保证自己的程序是没有bug的,每个程序都需要在不断测试的过程中去修复bug。

       在第一单元中,我的测试用例主要是分为正常情况以及极端情况,正常情况测试基本功能,保证基本逻辑是正确的,而极端情况则是测试边界情况是否正确。第一单元主要的侧重点是错误情况的判断,因此构造了大量错误情况的测试用例。

       第二单元中,因为该单元是多线程,而多线程程序是不能进行断点debug的,因此在测试时,很多错误往往不能复现,因此需要检查代码的设计思路和逻辑来检查错误。我在这个单元构造的测试数据主要是为了检测可捎带请求是否可以正常执行,电梯的运行是否符合期望,线程可以不可以正确结束。

       第三单元中,我的测试用例主要都集中在正确性的测试上,没有进行压力测试,没有构建边界数量的测试用例去测试运行时间,所以导致强测时出现超时的情况。同时,在这个单元的教学过程中,老师建议我们使用Junit去自动进行测试,但是我只是在最后写博客总结的时候用了一下Junit,之前在测试程序的时候并没有使用。

       第四单元,因为可能出现的情况过于繁多,因此我是通过构建局部的测试用例来进行测试的。对于特殊情况,构建特殊的用例,测试结果是否正确。

       总的来说,我对自己程序的测试依旧不够充分,每次都是写了一些测试用例之后,测试没有问题,便不想继续测试了,但实际上可能还有很多bug。同时在构建测试数据的时候,要脱离开自己程序的实现逻辑,根据需求去构建各种测试数据,避免无法检查出自己逻辑上的错误,在测试的时候也要先进行分类,争取将所有的类别都覆盖到,尽可能的做到测试的全面性。

四、课程收获

       这个学期因为有了OO课而变得充实了许多,每周都被OO的project填充着,我也从这门课程中收获颇丰。

       首先是程序架构方面。我不再看到题目就开始写程序,反而会在看到需求之后先去思考程序可能的架构,同时考虑可能会有哪些扩展,在构建程序的时候通过使用面向对象的思想去增加程序的可扩展性,尽可能去实现一个可扩展的架构。

       其次是面向对象思想方面,在上这个课程之前我还不太了解程序还可以这么编,然而在经过一个学期的学习之后,可以从更宏观的角度去思考问题,从对象和对象的层次去思考两者之间的交互,对于对象和类的区别、继承和接口实现、行为的抽象、多线程等等都有了更深层次的理解,也会进行一些简单的使用。

       在代码方面,这个学期大概是我的代码量最多的一个学期,每周都会有一个project,每个project都会实现新的功能,这种锻炼提高了我的代码编写能力和代码测试能力。同时,每次project都会检查代码风格,从第一次的最后再去调整代码风格,到后来我在写代码的过程中就会下意识地去检查代码风格。因为代码风格的限制,也使得我在编写方法的时候去检查自己的方法是否过于复杂,是否需要进行拆分,提高了我的方法的功能单一性。

       在测试方面,OO课程大概是我写测试用例最多的课程,每次都会绞尽脑汁地去想可能的情况,锻炼了我构建测试集的能力,同时还学会了一些自动化测试的方法,例如Junit。

       最后,通过OO课程我还学到了很多新的知识,比如熟悉了Java语言,学写了如何去阅读JML规格,编写对应的JML规格,学习了如何去画UML的各种图,学习了使用StarUML,学习了如何编写多线程程序,因为每周都在接触新的任务,所以每周也都在学习新的知识。在学习新知识的同时,还复习了一些旧的知识,比如地铁系统中就用到了图论的知识,多线程中则涉及到了操作系统中线程的概念。

五、三个具体改进建议

  • 1. 希望可以在实验课后,给出关于实验课结果的反馈,像是提交程序的实验课,希望可以给出测试结果,同时将每周的实验课的题目放出来,给出一些标准答案或者范例,便于同学们在课下发现和解决自己的问题。
  • 2. 希望在评测机可以提供测试CPU运行时间的平台,有的作业对于CPU运行时间有要求,但是本地的测试时间与评测机的CPU运行时间并不相同,因此希望评测机可以给出相关的测试窗口,便于同学们测试自己的程序。
  • 3. 希望指导书的要求可以更加明确,比如对于每一种情况举出一个具体的例子,很多时候因为纠结于模棱两可的情况而耽误了许多时间,同时如果在讨论区进行明确,可能会有不经常看讨论区的同学没有看到。

猜你喜欢

转载自www.cnblogs.com/gaojiaxuan1998/p/11079423.html