OO 第四次总结
这两次作业训练同学结合starUML工具解析UML图的能力。
1.1 UML第一次作业
第一次作业解析UML图的要求是类图。需要完成对类,类的属性,类的方法,类间继承,类实现接口等UMLElement的查询。本次作业实现难度不大,但是UMLElement的拆解比较细碎,且输入是乱序的,不能产生数据依赖,因此为完成任务需要构建大量数据HashMap。

为了让代码更加美观(类不超过500行),我从主类MyUMLInteraction中拆除构造方法Build类,对于每种传入的Uml元素,通过Build类实现信息的记录。这个设计优势在与把构造方法的功能拆出,保障了单一责任原则,带来的麻烦是,MyUMLInteraction类的众多数据结构要作为参数传到build类,书写上比较麻烦。
直接查询构造方法统计出来的信息的几个方法,getClassCount、getClassOperationCount、getClassOperationVisibility无需多加赘述,直接查询相关Map就可以实现。
仅与类间继承相关的查询需求包括 getClassAttributeCount、getClassAssociationCount、getClassAssociatedClassList、getClassOperationVisibility、getClassAttributeVisibility、getTopParentClass实现思路也很直观,由于Java8不支持类之间多继承,子类只有唯一父类,因此通过son2dad一路向下,同时对路上的信息做相应的缓存,可以很好的完成需求。
稍微麻烦一些的是getImplementInterfaceList,这个麻烦是接口之间多继承带来的。把继承关系当作有向图结点之间的边,用图的思路解决问题就很容易了。我用bfs实现的这个查询功能。
这次作业整体难度不大,作业要求歧义不大,就是数据结构多而琐碎导致代码写起来体验较差,而且容易马虎出错。我本次作业就是由于一个非直接查询数据机构的方法中,忘记调用一个private二次信息收集的方法,导致强测错了一个点。
1.2 UML第二次作业
第二次作业中,查询要求增加了对状态机以及顺序图的查询,同时对于第一次作业出现过的类图,新增了重复命名检查,循环继承检查,重复实现接口检查的需求。第二次作业相对于第一次作业,上手更为困难,完成作业的思维量也增加了一些。

在第二次作业中我将功能更细致的拆出去。Graph类用来处理新增加的对状态图和顺序图的查询,Check类实现对类图进行Rule1,Rule2,Rule3的查询,Build类服务于构造方法中对UMLElement的信息的初始存储,主类依旧完成对类图的先关查询,为了满足类不超过500行,对于与attribute有关的查询被单独放到了count类。拆类将功能更加细致的划分出去,但是更多的map结构在类之间传递也为写代码带来困扰,并且有一个类方法依赖的数据结构过多,最终传入9个参数,checkstyle没有过。
在第一次作业的基础上新增的查询状态图和查询顺序图的需求难度不大,在构造方法中把这两个图有关数据类型之间的关系保存下来,在getStateCount、getTransitionCount、 getParticipantCount、getMessageCount、 getIncomingMessageCount直接查询即可,getSubsequentStateCount中后继状态之间的关系类比多继承关系,用bfs解决也是轻车熟路。
这次实现需要费一些脑筋的是三个检查规则。
public class Check { public HashSet<AttributeClassInformation> check002( HashMap<String, UmlClass> claid2cla, HashMap<String,HashMap<String, HashSet<UmlAttribute>>> cla2attrName2attr, HashMap<String,String> end2end, HashMap<String,HashSet<String>> claid2endid, HashMap<String,UmlAssociationEnd> endid2end) private void dfsClass(String origin,HashMap<String,String> son2dad, HashSet<String> lis,HashSet<String> bla,String now, HashSet<String> mark) private void dfsInterface(String origin, HashMap<String,String> son2dad, HashMap<String,HashSet<String>> int2dad, HashSet<String> lis1, HashSet<String> bla1,String now, HashSet<String> ma1) { public HashSet<UmlClassOrInterface> check008( HashMap<String,UmlClass> claid2cla, HashMap<String,UmlInterface> interid2inter, HashMap<String,String> son2dad, HashMap<String,HashSet<String>> int2dad) private void bfsInterface(String interid, HashMap<String,ArrayList<String>> son2dadList, HashSet<String> bad) private void bfsClass(String claid,HashMap<String,String> son2dad, HashMap<String,ArrayList<String>> son2dadList, HashSet<String> bad, HashMap<String,ArrayList<String>> cla2interid) public HashSet<UmlClassOrInterface> check009( HashMap<String,UmlClass> claid2cla, HashMap<String,ArrayList<String>> cla2interid, HashMap<String,UmlInterface> interid2inter, HashMap<String,String> son2dad, HashMap<String,ArrayList<String>> son2dadList) }
Rule1就是对于已经存好信息的数据结构进行查询即可。对于Rule2,由于接口与类继承特性的区别,分别检查接口与类是否满足要求。对于循环继承,考虑进行dfs递归,我的初始版本dfs设计的不好,思路受限于哈密尔顿图的dfs算法,导致一些冗余重复的递归发生,对于继承分支多的接口检查时会超时。经过思考与同学讨论,意识到这个地方的dfs与哈密尔顿图的dfs要求有些许差别,因此实现上细节应该改进。对于Rule3,与检查类有几个接口的思路类似,使用bfs并适当缓存信息即可。
1.3 UMl作业总结
这两次作业构造的数据结构前所未有之多,但完成任务的思路难度不大。相比于第二单元、第三单元的作业,我在这个单元的作业中格外注意把需求细化,符合单一责任原则,避免方法承担太过繁复的任务。代码整体看起来清爽了许多。UML类图不经过解析包处理时,数据框架看上去庞大可怕,但是通过id找到source target parent这些关系还是很有助于理解UML图之间的层次关系,所属关系。
二、四个单元架构设计与OO方法理解演进
2.1 第一单元多项式求导作业回顾
第一单元的任务是熟悉面向对象编程的方式,体会面向对象的优势。
第一单元的内容是多项式求导。前两次作业中,我依据表达式的层次(加减乘除混搭的表达式、项、因子)把整个程序拆成类。拆成类的目的也只是在多项式类中方便用一个ArrayList将这些类的对象管理起来,是徒有其表的面向对象,实质实现过程依旧是面向过程的。
第三次作业复杂度高了一些,我的思路是把嵌套关系用树表示,具体实现是用递归的方法。第三次作业中,我尝试练习使用继承,幂函数、三角函数继承factor类,add,mul,nest继承combine类……事实证明,第三次作业确实是面向对象继承与实现接口发挥优势的平台,表达式不同层级的子式在求导过程中需要扮演的角色固定,所拥有的属性也是确定的。
后来我又思考了一下第三次作业的架构,我认为不用继承,而用接口可能更加合理一些。
2.2 第二单元电梯作业回顾
第二单元的主题是多线程。
三次作业的总体模型是生产消费这模型。前两次作业的两个线程分别是单部电梯线程、输入线程,共享对象是请求队列。从第一次作业到第二次作业的一个难度跨度是电梯有了一定的“调度策略”后,不再顺序服务,线程安全结束的问题。第三次作业出现了三部电梯,四个线程分别是输入线程,三个电梯线程,共享对象是请求队列。第三次作业的共享对象类(线程池)中,把请求分派给3个电梯的方法,我写的繁杂冗长,把多个功能糅杂在一个方法中,在电梯线程的run方法中,我也直接将电梯运行的许多细节直接放在该方法中,没有很好的封装。整个第三次作业的代码风格就像一个被打坏了塑料外壳的电源插板,花花绿绿的电源暴露在眼前,造成了极大的不适感。
2.3 第三单元 JML作业回顾
第三单元的主题是理解JML语言
到了第三单元,我终于为了不TLE抛弃了ArrayList,投奔HashSet,HashMap……投奔了Hash系列,重写equals方法,compare方法的过程让我加深了对继承Object类的理解。
第三个单元的JML是一套有自己的语法体系,逻辑严密的建模语言,JML的描述要求一个类,一个方法只能完成一定限制的职责,我前两个单元的还多少保留着面向过程设计思维的建模方式在JML语言的制约下是行不通的。前两次作业对一个容器进行解析查询,总的实现思路不复杂,留心“缓存”,留意空指针就不会有大问题。第三次作业的思维含量很高,第三次作业的出现让前两次愉快而让人摸不着头脑的“容器”落地,与实际问题挂钩。面对第三次作业中最低票价,最低不满意度等计算需求的出现,我经历过一次重构,最后选择拆点。根据不同的计算需求,设定拆点后得到的图边的长度,并在这个图中通过迪杰斯特拉计算最短路径,进而得到最低票价或最低不满意度。第三次作业拆点的时候,单独建立Graph类,我对于面向对象”类“的意义有了更深刻的体会。
2.4 第四单元UML作业
第四个单元的主题是UML图解析。
本单元作业的实现框架已再上文第一部分说明,因此不再赘述。
这个单元隐性涵盖了之前几个单元练习到的知识。UML图中表现的是类与类的继承关系,接口与接口的继承关系,类对接口的实现关系。在完成第四单元的作业时,第三个单元的练习中学到的用拆分类,拆分方法的方式实现单一责任原则的体会得到了进一步落实,代码整体风格清爽了不少。第二个单元的多线程,实现的电梯调度问题,以及课上实验中做的出租车调度问题其实都可以用UML图形象的表示。
三、测试理解与实践的演进
OO学习的过程中,我对于程序测试的理解逐步深化。
第一个单元中,我的测试方法笨拙原始,人工程度极高。几次作业,我随机输入一些表达式,用MATLAB计算结果,与自己的程序输出结果比对,进行自测。由于前两次互测的时候允许返回“WRONG FORMAT”是否正常判断的错误,我在互测阶段阅读别人判断错误输入格式部分的代码,揪住漏洞,针对WRONG FORMAT进行攻击。第三次作业禁止对WRONG FORMAT进行攻击后,我也是随便输入一些我认为怪异的数据,比较结果判定正确性。
第二个单元的测试手动输入样例复杂性高,且效率极低。我开始利用循环生成随机测试数据的方法对程序进行自动化测试,并相应的利用输出结果测试正确性的代码对输出结果进行评判,如果输出无误,则自动投入下一组测试数据。第二个单元的测试模式是自动化的,但是测试工作的重点放在对整个project的测试,范围广而针对性差。
第三个单元的测试手段更加丰富。第三个单元的输出结果相对于第二单元,确定性更高,因此可以沿用第二单元自动生成测试数据的方式,并直接使用对拍器验证正确性,无需构造输出数据正确性判断函数。除了“整体测试”,回归到代码本身的逻辑层次,利用JUNIT,TestMe插件可以针对某个类实现单元测试,将测试的范围细化,使测试更具有针对性。
第四个单元利用自动生成数据进行外部测试难度较大。两次作业我都是针对几个坑点,如第一次作业的实现接口数目统计,第二次作业循环继承的判断,重复继承的判断,构造特定的UML图,针对性地检查对应的方法实现是否存在问题,将测试的范围缩小,将测试的精准度增高。
四、课程收获
首先,一学期的OO训练中,我对面向对象编程的理解一步一步加深提高。之前用Python学习数据结构的时候,用着一门面向对象的语言,却鲜少关注面向对象的特点与优势,更多的是记忆算法。这一学期的OO课,我对于面向对象的建模方式,继承,接口等面向对象专属的特征逐渐熟悉,并感受到了其中的精妙。最神奇的是,面向对象的思维方式甚至渗透到我的日常生活,前一阵和别人讨论一个网游,我居然很神奇的在脑海中勾画某几种角色应该继承什么类,他们的父类应该定义什么属性,什么方法……
其次,OO课程让我发现了Java的强大。Java的HashSet,ArrayList,HashMap等数据结构各具特色,带来许多便利。Java编译环境对诸多插件,如单元测试的Junit,分析复杂度的Calculate Metrics,绘制类图的diagram等的支持更是一个成熟的编译器应当具有的品格。
再次,OO课程加深了我对于测试的理解,几个单元的测试中,我从最开始的针对整个project用最笨拙的手动测试,到学会自动化测试,再到将测试精准化到某个单元进行单元测试……我对于代码测试的认识从单一层次演变到了多层次。
最后,OO课程为我更真实的还原了一名真正的程序员,而不是一个上编程语言课的大学生,将要真正面对的问题的打开方式。与C语言课,数据结构课截然不同,OO的作业从不是以一道表意明确的十行以内文字描述的题展现的,每一次作业都是以课程组与助教团队提供的指导书形式呈现,一次作业不是一个简单的题,而是一个要完成多个层次任务,并需要针对一些异常情况进行处理的真正的工程。这就有点像真正进入工作后,我们会面对的客户需求,只不过,我相信OO课程的指导书形式会比真正的客户需求“友善”的多。
五、改进建议
①首先关于课程训练内容,我觉得四个单元的训练的内容都很有意义。但是我个人感觉,第二单元的多线程训练不够,有些”浅尝辄止“,目前的几次作业全部对方法加sychronized锁就可以实现,而多线程的一些更为灵活的手段并没有得到很好的训练。对于多线程的几种模式,目前的作业也是用生成消费者模型就可以解决了,而多线程的一些其他模型也没有训练到。
②其次关于指导书。十分感谢课程组与助教大大每星期出指导书的辛苦付出。但是最后一个单元的指导书,内容不是很明确,给出的例子也比较少。希望对于UML图的几次作业指导书能在具体的指令后多给一些例子,最好能把图也附上去,减少歧义,也降低同学们因理解错指导书的意思而反复修改代码的概率。
③最后关于课程网站平台的建设。最后两个单元涉及到CPU运行时间限制的问题,很多同学在本地很难进行CPU运行时间测试,希望课程平台能开一个CPU运行时间测试的平台,让同学们投喂自己的数据,然后显示CPU运行时间。
最后的最后,很感谢OO这门课,衷心祝福OO课程越来越好。

浙公网安备 33010602011771号