OO第四单元及课程总结

第一部分 第四单元UML类图作业总结

第一次作业

架构分析

第一次作业针对UML类图进行解析,需要完成一批查询指令。具体包括类的个数、操作的个数、属性的个数等。这次作业其实在考验对于类图元素的熟悉程度。只要厘清UML类图的层次结构,其实这次作业并没有太多的实现难度。

由于最开始并没有想到会有这么多的查询指令,于是就直接一个MyUmlInteraction莽到底。好在最后还是控制在了500行大关。整体的架构是在构造函数中遍历读入的UmlElement类,对不同的element进行分支处理,从而将获取到的信息全部保存下来。例如对于操作中的parameter类型,我们进行如下操作:

if (type == ElementType.UML_PARAMETER) {
    UmlParameter p = (UmlParameter) elements[i];
    UmlOperation o = (UmlOperation) id2ele.get(p.getParentId());
    HashSet<String> h = opeId2paId.getOrDefault(o.getId(), 
            new HashSet<>());
    HashSet<String> ht = new HashSet<>(h);
    ht.add(p.getId());
    opeId2paId.put(o.getId(), ht);
}

将parameter的ID信息添加到对应的操作中去,完成了信息的提取。另外由于最初并未考虑读入顺序的问题,所以处理方法是遍历了两次UmlElement。这个问题在第二次作业就顺利解决了,因为只要只处理ID,就不会有找不到相应元素的情况。

而之后的不同查询指令,则是从我们的一堆存储信息的数据结构中找到对应的信息。其中查找所有父类或所有属性,都是建图采用bfs广搜,不赘述。

类图与复杂度分析

Methodev(G)iv(G)v(G)
Helper.bfsClass(String,HashMap<String,   HashSet<String>>,HashMap<String, String>,HashMap<String,   HashSet<String>>) 1 4 4
Helper.bfsInterface(String,HashMap<String,   HashSet<String>>) 1 4 4
Helper.classOpeVis(UmlClass,HashMap<String,   HashSet<String>>,String,HashMap<String, UmlElement>) 3 6 7
Main.main(String[]) 1 1 1
MyUmlInteraction.MyUmlInteraction(UmlElement...) 1 30 30
MyUmlInteraction.bfs() 1 4 4
MyUmlInteraction.check(String) 3 1 3
MyUmlInteraction.getClassAssociatedClassList(String) 1 2 2
MyUmlInteraction.getClassAssociationCount(String) 1 1 1
MyUmlInteraction.getClassAttributeCount(String,AttributeQueryType) 3 3 3
MyUmlInteraction.getClassAttributeVisibility(String,String) 3 1 3
MyUmlInteraction.getClassCount() 1 1 1
MyUmlInteraction.getClassOperationCount(String,OperationQueryType) 7 8 9
MyUmlInteraction.getClassOperationVisibility(String,String) 1 1 1
MyUmlInteraction.getImplementInterfaceList(String) 1 2 2
MyUmlInteraction.getInformationNotHidden(String) 1 4 4
MyUmlInteraction.getTopParentClass(String) 1 1 1
MyUmlInteraction.hashadd(HashMap<String,   Integer>,HashMap<String, Integer>) 1 3 3
MyUmlInteraction.judgeOperation(UmlClass,UmlOperation) 3 6 10

 

可以看到一个类莽穿的结果就是复杂度崩盘。。。

bug分析

一个400多行乱七八糟的类的调试是令人抓狂的。不过好在这个程序是分块的,也即是每一部分其实互不相关。笔者在中测中发现了HashMap不存在相应key导致的NullPointer异常和寻找继承关系时陷入的死循环。很快就改掉了bug,也完成了相应的对拍。顺利a掉了强测。

第二次作业

架构分析

第二次作业在第一次作业的基础上添加了check模块(三条检查规则)、顺序图、状态图三方面问题。笔者采用了组合,将三个类分离,并在最终的GeneralInteraction中实例化了三个对象,依次调用各自的方法。

在增加了大量数据基本限制之后,顺序图和状态图的实现就是按部就班的解析、记录、输出信息的过程。而难点其实在check模块上。笔者首先将继承和实现视为一种关系,并在相应图中连线。R002采用了dfs寻找环路的方法,R003采用了寻找全部父类再查重的方法。其实还是图论的知识(还债)

具体以R003为例

public void checkForUml009() throws UmlRule009Exception {
    for (String s : matrix.keySet()) {
        findFather(s);
    }
    HashSet<UmlClassOrInterface> outset = new HashSet<>();
    for (String s : id2fatherid.keySet()) {
        ArrayList<String> fatherid = id2fatherid.get(s);
        HashSet<String> temp = new HashSet<>(fatherid);
        if (fatherid.size() != temp.size()) {
            outset.add((UmlClassOrInterface) id2element.get(s));
        }
    }
    if (!outset.isEmpty()) {
        throw new UmlRule009Exception(outset);
    }
}

这一段是遍历所有类与接口,寻找所有fatherid,并与对应的set进行对比,将不一致也就是出现重复fatherid的类或接口添加outset,最后抛出异常。

类图与复杂度分析

Methodev(G)iv(G)v(G)
Helper.bfsClass(String,HashMap<String,   HashSet<String>>,HashMap<String, String>,HashMap<String,   HashSet<String>>) 1 4 4
Helper.bfsInterface(String,HashMap<String,   HashSet<String>>) 1 4 4
Helper.classOpeVis(UmlClass,HashMap<String,   HashSet<String>>,String,HashMap<String, UmlElement>) 3 6 7
Main.main(String[]) 1 1 1
MyUmlClassModelInteraction.MyUmlClassModelInteraction(UmlElement...) 4 30 31
MyUmlClassModelInteraction.bfs() 1 4 4
MyUmlClassModelInteraction.check(String) 3 1 3
MyUmlClassModelInteraction.getClassAssociatedClassList(String) 1 2 2
MyUmlClassModelInteraction.getClassAssociationCount(String) 1 1 1
MyUmlClassModelInteraction.getClassAttributeCount(String,AttributeQueryType) 3 3 3
MyUmlClassModelInteraction.getClassAttributeVisibility(String,String) 3 1 3
MyUmlClassModelInteraction.getClassCount() 1 1 1
MyUmlClassModelInteraction.getClassOperationCount(String,OperationQueryType) 7 8 9
MyUmlClassModelInteraction.getClassOperationVisibility(String,String) 1 1 1
MyUmlClassModelInteraction.getImplementInterfaceList(String) 1 2 2
MyUmlClassModelInteraction.getInformationNotHidden(String) 1 4 4
MyUmlClassModelInteraction.getTopParentClass(String) 1 1 1
MyUmlClassModelInteraction.hashadd(HashMap<String,   Integer>,HashMap<String, Integer>) 1 3 3
MyUmlClassModelInteraction.judgeOperation(UmlClass,UmlOperation) 3 6 10
MyUmlCollaborationInteraction.MyUmlCollaborationInteraction(UmlElement...) 1 6 6
MyUmlCollaborationInteraction.checkForInteractionName(String) 3 1 3
MyUmlCollaborationInteraction.checkForLifeLineName(String,String) 3 1 3
MyUmlCollaborationInteraction.getIncomingMessageCount(String,String) 1 1 1
MyUmlCollaborationInteraction.getMessageCount(String) 1 1 1
MyUmlCollaborationInteraction.getParticipantCount(String) 1 2 2
MyUmlGeneralInteraction.MyUmlGeneralInteraction(UmlElement...) 1 1 1
MyUmlGeneralInteraction.checkForUml002() 1 1 1
MyUmlGeneralInteraction.checkForUml008() 1 1 1
MyUmlGeneralInteraction.checkForUml009() 1 1 1
MyUmlGeneralInteraction.getClassAssociatedClassList(String) 1 1 1
MyUmlGeneralInteraction.getClassAssociationCount(String) 1 1 1
MyUmlGeneralInteraction.getClassAttributeCount(String,AttributeQueryType) 1 1 1
MyUmlGeneralInteraction.getClassAttributeVisibility(String,String) 1 1 1
MyUmlGeneralInteraction.getClassCount() 1 1 1
MyUmlGeneralInteraction.getClassOperationCount(String,OperationQueryType) 1 1 1
MyUmlGeneralInteraction.getClassOperationVisibility(String,String) 1 1 1
MyUmlGeneralInteraction.getImplementInterfaceList(String) 1 1 1
MyUmlGeneralInteraction.getIncomingMessageCount(String,String) 1 1 1
MyUmlGeneralInteraction.getInformationNotHidden(String) 1 1 1
MyUmlGeneralInteraction.getMessageCount(String) 1 1 1
MyUmlGeneralInteraction.getParticipantCount(String) 1 1 1
MyUmlGeneralInteraction.getStateCount(String) 1 1 1
MyUmlGeneralInteraction.getSubsequentStateCount(String,String) 1 1 1
MyUmlGeneralInteraction.getTopParentClass(String) 1 1 1
MyUmlGeneralInteraction.getTransitionCount(String) 1 1 1
MyUmlStandardPreCheck.MyUmlStandardPreCheck(UmlElement...) 1 8 8
MyUmlStandardPreCheck.checkForUml002() 2 8 9
MyUmlStandardPreCheck.checkForUml008() 2 4 5
MyUmlStandardPreCheck.checkForUml009() 2 4 5
MyUmlStandardPreCheck.findCycle(String) 3 4 4
MyUmlStandardPreCheck.findFather(String) 2 3 4
MyUmlStateChartInteraction.MyUmlStateChartInteraction(UmlElement...) 1 14 14
MyUmlStateChartInteraction.checkForMachineName(String) 3 1 3
MyUmlStateChartInteraction.checkForStateName(String,String) 3 1 3
MyUmlStateChartInteraction.getStateCount(String) 1 1 3
MyUmlStateChartInteraction.getSubsequentStateCount(String,String) 1 4 4
MyUmlStateChartInteraction.getTransitionCount(String) 1 1 1

 

UmlModel类图部分直接照搬上次作业,所以复杂度依旧爆炸。其他部分复杂度控制的还行,因为查询内容少。。。

bug分析

本次作业中测测出了自己的一些笔误bug,其他没有出现什么大问题。也是顺利对拍,顺利a掉强测。

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

第一单元 表达式求导

表达式求导对于我这样一个刚入门OO的菜鸡来说,其实是具有巨大挑战的。即使是第一单元的求导是循序渐进,功能化一步步增强,我仍然在第三次作业中感受到了极大的压力。因为第三次作业的功能性实现本身就存在难度,而WF判定对于完全迷失在架构设计的我来说,也是及其耗费时间的过程。

第一单元教会我最多的是java语法和面对繁杂的鲁棒性测试的重要性。过去的程序题大多完全没有考虑过程序的鲁棒性。面向对象本身倒是没有掌握太多精髓。封装、继承、多态也就只是勉强运用了封装的思想,把多项式、项、因子之类的拆解封装,本质上还是面向过程的代码。但也还算是初步了解了关于OO的皮毛知识。

第二单元 多线程电梯

第二单元就是神圣的多线程单元,佛系bug佛系de。这对于从未接触过多线程编程的我来说又是一次晴天霹雳。这一单元几乎放弃了非平民玩家向的性能分,还算苟的好,没有翻车。

这一单元的架构相比于前一单元稍微好一些,没有第一单元那样的大规模重构。但是对于程序尤其是多线程程序的把控力太差,导致要不停地在原有代码结构上修修补补,因此架构设计并不出色。尤其是第三次作业的魔鬼多电梯调度,要求固定的楼层,我自己的死板架构导致了优化几乎没有可能。

这一单元对于OO方法理解只能说略有长进,且长进来源于编码经验的增长,主要学会的其实还是多线程编程的基本方法,尤其预防死锁的方法。

第三单元 JML规格化设计

这一单元表面上是一个JML单元,实际上是一个算法与数据结构单元。这一单元的架构其实大同小异,毕竟已经有官方接口给大家约束了实现方法的规模。前两次作业还仅仅是完形填空,照葫芦画瓢,按部就班就能完成。最后一次作业就严格限制了程序的CPU时间,且加入了很多动态权的有向图建模问题(也就是所谓的换乘问题)。笔者既想完成统一化建模,又希望程序能有较好的性能,奈何代码掌控力实在太差,导致第三次作业的架构几乎是全面崩盘,我不折不扣地写了一坨屎山。屎山完成之后,就是艰难的debug阶段。在挣扎着过了对拍器之后,我也是再也不敢动自己的代码。这种切实体会的感觉还是很不好受的。

好在最终性能也不差,苟过了强测。对于OO理解还是在这一单元得到了比较多的提高。主要是对给出的标程进行了一番研读,发现原来这才是面向对象,每一个方法优雅的行数真是让我有些吃惊。如果每一个方法都能足够简单,只负责方法本身需要的内容,那么代码者就可以很容易把控住体系庞大的代码。而且本身JML就有益于整个程序框架的拆解,所以这一部分虽然是还数据结构的债,但从中学到的OO知识其实不少。

第四单元 UML图

UML图这个单元接近收尾,作业也减少了一次。这个单元的作业深度普遍不深,但是广度很大,需要我们完整掌握UML的层次结构。当然UML也侧面在给我传递着OO的思想。这次作业虽然体系结构还是很莽,但是类中的各个方法几乎都相互独立,各司其职。类与类之间也不会相互杂糅交叉,导致发现bug之后不仅能快速定位,而且也能修改过来。

UML图这一部分也教会了我不少OO方法。其实更像是第三单元学会之后的一次小实践。尽管如此,我还是没用上继承、多态这样的高端技巧,一是不熟练容易出问题,二是本单元难度不大,确实不存在过于复杂的层次关系。

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

第一单元 表达式求导

第一单元的测试主要停留在人为构造数据和阅读代码的静态检查上。这个时候单纯的OO玩家并没有意识到自动化测试的重要性和可怕之处。测试阶段采用了边界测试和压力测试,不断“人为”构造极端数据,例如臭名昭著的“\v”和加减号控制上。压力测试也就是测试整个程序有没有在过大表达式的输入上爆栈翻车。

另外第一单元学会的重要事情就是永远不要让程序中途crash,这是很严重的。

而在互测中,首先也是运用自测的边界数据尝试找出bug,而其次是人工阅读互测屋中同学的代码,去寻找漏洞。人工阅读7份代码其实是非常消耗时间的事情。看别人的代码永远是难懂的,所以这种耗时烧脑的事情其实在后面的OO体验中越来越少了。

第二单元 多线程电梯

随着课程的深入,讨论区大佬对于自动化测试的认真教导,让我意识到了自动化测试的重要性。通常情况下,编码者自己能够想到的边缘数据,编码者就已经意识到了这方面的bug,一般能在实现过程中就加以杜绝。而自动生成大量随机数据能够真正有效率的发现bug。那么自动化测试对于我来说难度其实不小,所以我们组成了一个小组,一起分工合作,合力搭建起了第一个评测姬。这个评测姬也帮助发现自己的死锁问题,帮我在第三次作业中捡回了一条命。

尽管如此,自动化测试的数据是大量随机的,缺乏了一些针对性。一些边缘样例还是需要人为构造。

第三单元 JML规格化设计

这一次的自动化测试其实比较好做,因为输出是确定的,所以我们就采用了对拍器的方式。生成大量随机数据之后直接利用室友、利用同学、利用不同的jar包进行正确性对拍。从而实现了双赢的强测得分。

然而悲剧的是,第一次作业由于过于简单,大多数人没有把它放在心上。我也没有进行对拍。然而就是这样看似简单的作业,我也没有经过太多的静态检查,导致了自己的强测疯狂炸点。这个bug其实非常严重,我不管path是否加入过都视为第一次加入。这个bug其实很好找出。这也预示了自己任何不测试的代码都是不可靠的!

因此之后的每一次都经过了或多或少的测试。

第四单元 UML图

这一单元倾向于单元化测试,所以也很适合对每一个模块进行数据检查和静态阅读代码。我也对自己的代码分模块进行了一遍检查,发现其中并没有逻辑错误之后通过了中测的初步测试。

这一单元还是自动化生成随机数据+对拍器的模式。由于第二次作业正好赶上离散二的考试,所以本来准备放弃自动化随机数据的生成。好在课程组延时了12小时,我们也利用了这一段时间搭起了一个简易的评测姬,不管这个评测机没有帮助我找到bug。

第四部分 课程收获

OO这门课说起来,让人又爱又恨。毫不夸张的说,OO就是一个冷酷无情的周末吞噬机器。这学期的OO课几乎疯狂吞噬了我的美好周末时间。每个周末都在思考自己这次作业的架构和实现细节。

但是OO教会人的东西真的不少。

  • OO部分:教会了我封装、继承、多态的基本思想,教会了我多线程编程预防死锁的方法,教会了我JML语法和UML图建模。

  • 编码部分:学OO课之前,我其实没有太多的编码经验,最多也就是之前的课上的某个大作业。写代码这件事其实也是非常吃经验的。写的多了,整个人的心态都发生了变化。现在自己的编码能力虽然还是很弱,但是面对具体问题,不再会紧张害怕。而是能够从问题中有效拆解提炼信息,并且对整个实现有大概的印象。

  • 算法部分:切实掌握了很多在数据结构中学到的算法。也就是还了数据结构的债。

  • 设计部分:理解了设计层次化的重要性。之前拿到问题,第一反应都是程序框图式的面向过程思维,是如何从输入到数据处理到输出的全套流程。现在了解了一些面向对象的皮毛之后,想的就是如何拆解问题的具体部分,输入输出层应该做什么,数据处理包括哪些部分,这些部分之间具体有哪些联系等等。

  • 测试部分:学会搭建评测机和对拍器,也就是自动化测试的重要性

  • 团队合作部分:OO课的顺利平稳度过,其实也依赖于小团队的互相帮助,不管是找bug,贡献测试数据,还是熬夜一起完成自动化测试,都是非常宝贵的经验。期待今后的团队编程体验。

第五部分 面向课程的建议

  • 作业内容部分:建立提前JML规格化设计,本人在JML规格化设计中学的OO内容是最多的。根据自己的体会,希望能把JML的内容提前。提前既能让同学们一上手就看到最标准的面向对象程序的架构,又能一定程度上降低难度,不会出现还没有架构概念,就在表达式求导单元又是鲁棒性又是递归求导中迷失自我。

  • 指导书部分:希望能少量公布后续指导书。课程组希望我们能够在踩坑中提高自我,但我觉得指导书的适当公布可以给大家指明方向,不至于大规模重构。虽然大规模重构还是我自己太菜了(

  • 实验课部分:实验课的体验不是特别棒。感觉这门课的作业和实验课完全是两种画风,实验课既没有明确具体的要求,也没有完成之后的标准答案,让人摸不着头脑。

  • 互测部分:机制变成多人博弈之后,看代码变成了一件很累的事情。大多数同学也不会认真阅读七份代码,希望可以适当调整互测屋人数。另外老师课上说的机器人投放其他组的hack样例是一个不错的想法。我觉得很棒!

最后的最后

这门课的体验总体来说非常良好,可能是期望太低。但不得不再次感谢助教老师的辛苦付出。虽然周末吞噬机器无情地鞭挞过我的灵魂,但是确实获益匪浅。这也算是我在计算机编程方面的第一门比较正式的编码课。感谢OO让我顺利度过了新手村,获得了大批的编码经验。

希望OO的未来越来越好,也希望自己能继续直面将来的一次次挑战。

自勉自勉~~

posted @ 2019-06-24 15:57  xxj2017  阅读(147)  评论(0编辑  收藏  举报