Object-Oriented Programming Summary Ⅰ

Part 0: 前言

令人闻风丧胆的OO还是来了。并没有像名字的外表一样可爱,简直就是恶魔。

疯狂压榨OS的时间,周末无法休息,互测狼人机制

虽然网上骂声很多,就算改进到9012年还是有很多不足的地方,但和往年相比,已经有很大进步,也更加人性化了,我们并不能力求在我们这一届做到完美,这毕竟是一个长期长期长期长期又漫长的过程。既来之,则安之,既然无法改变规则,就让自己适应规则吧。

 

Part 1: 量度分析

接下来将细分开每一个板块来对我自己的三次作业来进行分析

DesigniteJava

DesigniteJava是一款专门分析代码质量的静态评估工具,并且识别潜在的质量问题。它会检查代码的架构,设计和代码的异味并且以csv的格式给出详细的度量分析。

对与分析Java这种面对对象的语言,我们这次主要看这几个参数:

  1. CC 圈复杂度

    用来衡量一个模块判定结构的复杂程度,圈复杂度越大说明程序代码质量低,难以测试和维护

  2. LCOM 方法的内聚缺乏度 值越大说明类内聚合越小

  3. FANIN 类的扇入 扇入表示调用该模块的上级模块的个数,扇入越大,表示该模块的复用性好。

  4. FANOUT 类的扇出 扇出表示该模块的直接调用的下级模块的个数,扇出大表明模块内复杂度高,但扇出过小也不好。

    总结就是,一个好的JAVA代码,设计要求是高内聚低耦合的,所以LCOM值要小,FANIN的值要大,FANOUT值合理。

     

    那么用这个方法看三次的度量结果:

     

      第一次作业:

    第二次作业:

    第三次作业:

 

可以发现,在Main函数中我们的圈复杂度都是相对较高的,这是因为我在Main中放了存放和预处理字符串的代码,这导致圈复杂度增高,而且确实大量的正则表达式和字符串替换难以后续阅读和维护,这是毋庸置疑的。看了其他同学的优秀代码,比如17373331的代码,Main中几乎没有什么实际的执行代码,只是在调用其他方法和处理异常,看优秀的代码给我的感觉就是非常清晰,在主函数中就明确告诉我了这个project是在什么的,合理的类的命名,简单易懂的返回值设定和处理异常的语句,符合OOP的设计。

果然,没有对比就没有伤害:17373331的静态分析:

    明显可以看出参数饱满了很多,不像我生成的几乎都是0,较小的LCOM和较大的FANIN和合理的FANOUT,果然花花的代码能被称作标杆范文。
 
此外,可以看到我的代码分析后的FANIN是几乎没有的,说明我的代码的复用效率低下,可能存在很多重复的代码,反观我的代码确实在这几次作业中,确实有很多服用的代码,就比如字符串为了防止被查WF,反复用正则表达式检查变换几次,甚至用上了 while (matcher.find())这样的循环查找,不仅复用率低下,其实代码的执行效率也非常低下。

  反思自己的代码质量,几乎惨不忍睹,甚至感觉用来查c等面对过程的语言得到的结果也相差无几,主要是以下几点原因:
  1. 阅读面对对象的代码太少,学习了理论但是无法自己亲手时间,可能具备了阅读不同的代码说出是不是符合面对对象的设计,但是自己却无法写出。

  2. 对质量的盲目追求。在分数的利益驱动下,自然地用面对过程的语言,也就是自己熟悉的语言来处理字符串,这样虽然代码不是很好看,甚至在第三次作业中为了判断各种WF情况而写了冗杂的正则表达式查询代码,导致主函数内方法函数超60行,代码风格质量严重降低,这时又只能通过分开建立不同的方法来分担正则式的长度,恶性循环导致风格逐渐下降,但是分数可以很好,别人几乎不能攻击我的WF,这也是对自己的代码风格和分数之间的trade-off之间,分数占了上风吧。

  3. 还没有形成剥离抽象层的思维,像zsa学长在讨论区的接口讲解非常清晰,举出了生动的例子——手机的充电接口来让我们理解。 但是总是在自己构思的时候就无法剥离出sin cos 和x 和constant 之间的共性特征和共性方法,所以也很少使用继承或者借口了,在最后一次的作业中强制要求使用后才尝试用接口的方法来解决求导的问题。

接下来查看自己的UML图,由idea自动生成:

第二次作业UML   



第三次作业UML 

第一次作业似乎无法生成UML,可能是因为太面向过程了一点,但我还可以看到第二次和第三次的,但是要我评价自己的类图,我十分羞愧,因为这完全是一个没有上过OO理论课的人都能写出来的东西,所以我大概讲一下我的类图在写代码时的构造吧:

在第二次的代码中我才用了四元数的方式,所以自己构建了两个class,分别是Trig 三元组储存x 和 sin 和 cos,还有Term 四元数在三元数的基础上增加了前面的常数项,由这两个class我们就可以对相同的相进行合并。并且尝试了多态的方法;在第三次的代码中采用了接口的方式,对所有项的求导进行了归一化,但是其实可以更加优化,那就是将所有的项归并到抽象类中,因为他们都有共同的属性比如内部的表达式(指sin和cos内部的值)还有幂次都是共通的,用抽象类的方法可以将他们更加内聚在一起,所以我也打算重新写一遍第三次的代码了,当然是建立在阅读第二次优秀的代码基础之上。

在第三次作业中,我也对static和final有了更进一步的了解。之前看到java的代码中很多的static所以没有融入自己的思考盲目static,这直接导致我第二次作业开始,开的类多了之后出现致命的错误,创建多个对象之后共同修改一个属性,导致了大片的错误输出,所以说还是实践出真知。

Part 2: bug分析

第一次作业:

第一次作业比较简单,当然自己用面对过程的语言做所以没有出什么问题,最后是数据点全过+0hacked,但是当然还有美中不足的地方,那就是性能分没有拿到满分,具体在于自己运用HashMap的方式存储结果,查询不到HashMap如何查找到特定value的值,即正数的值放到第一位,所以最后性能分未满,但是如果现在要我再交的话,我会利用先存进ArrayList的形式来做一个简单的排序,从而将正项第一个输出。

第二次作业:

第二次作业在强测中出了一个BUG,导致被分配到了C组中去,因为自己优化太过于匆忙,疏忽了HashMap在puts之前要先检查是否存在,导致相同的Key值出现了覆盖Value的情况,最后失分。其实自己写了一个专门puts进HashMap的方法,在方法中检查了Key值并进行合并,但是由于是ddl前的早上优化所以没有进行太多的测试,过了弱测和中测就没有多想,这个案例说明 侥幸心理 是万万不可取的。

在互测屋中还被发现了一个特殊的bug,在输入以运算符结尾的时候我不能判断出格式错误,导致被人狂刀,这也是和自己的测试策略有关,自己在自测的过程中偏向于对正确性进行检验,而忽略了错误形式的表达式的构建,导致失分。

第三次作业:

吸取了第二次作业被WF杀害的经验教训之后,几乎最后一天都在对格式判断进行改动,但是没有注意到助教的置顶帖中已经说明移除了WF的数据,有一点时间没有用到刀刃上的感觉,甚至有点把自己给误伤了。过多的特殊WF情况的判断导致自己对代码的结构已经无法理解,结果在强测中对正确的输入报了WF,痛失去一个强测点。此外,对括号内的处理也不够仔细,忽略了幂次也能带有负号的问题,导致表达式提取出现问题,结果丢分。

 

在第一单元最后一次的研讨课上,有位同学给予了我们一个开始工程的思路——先构建数据集(测试集),再下手打代码,起到打代码的时候有的放矢的高效。开始的时候我是有点不理解的,但是在反思自己的作业的过程中,发现第三次作业中我所犯下的错误完全是可以由一开始构建一个较为完备的测试集而规避的,而且纪老师说在后续的工程化代码中,提前构造测试集是一种非常有效防止严重漏洞的手段,所以我也考虑在重写第三次作业的时候也尝试这种方式。

 

Part 3: hack

对于查找别人的漏洞的方式,主要在于以下几点:

  1. 自己在打代码的时候,测试自己的代码时所出错的数据点都一一保留,这应该是算弱测。

  2. 寻找特殊的表达式的特征,具体可能是x-x或者多个+-号的形式。

  3. py或者java自动生成代码加上mma的带入测试。

  4. 寻找同学进行求助,每个人对同一问题的思考角度不同,hack别人的点一般有很大区别,所以是一个补全的很好的策略。

 

Part 4:重构的说明

重构其实不是一件丢人的事情,自从上学期计组以来,发现其实解决自己bug的最好最快的方式就是重构。重构可以帮助我们在原有的基础上进行改善,一方面验证我们对现有知识体系的更加深层的理解是否正确,另一方面也是对未来增添功能时候提供一种方便。在初学的阶段,我认为重构代码时非常常见的,也是一种高效的学习方式,虽然时间会花费很多,所以在第二次作业中我就采取了重构的方式,改掉了原有的面向过程的写法,采用了初级入门的面向对象,并且在第三次作业中也复用了第二次作业的体系逻辑,虽然增添删改了许多内容但其实也减轻了我挺大的工作量的。

 

以上便是本人对于前三次OO作业的博客总结,后续可能会贴上自己第二次作业甚至第三次作业的优化的方法的思考。

 

原文地址:https://www.cnblogs.com/CookieLau/p/10611234.html