OO第一单元总结

这一单元的主要任务是求导,难点一是在输入的解析,特别是第三次作业引入嵌套后;另一个难点就是如何层次化地表示整个表达式,既方便构建,又方便后续的求导与化简。

程序结构分析

主要使用idea的插件MetricsReloaded完成。首先介绍一些度量的概念与含义,这部分选取自一些博客的总结。

一些度量的含义

方法复杂度

  • ev(G):即Essentail Complexity,用来表示一个方法的结构化程度,范围在$[1,v(G)]$之间,值越大则程序的结构越“病态”,其计算过程和图的“缩点”有关。
  • iv(G):即Design Complexity,用来表示一个方法和他所调用的其他方法的紧密程度,范围也在$[1,v(G)]$之间,值越大联系越紧密。
  • v(G):即循环复杂度,可以理解为穷尽程序流程每一条路径所需要的试验次数。

类复杂度

  • OCavg:平均循环复杂度
  • WMC:总循环复杂度

第一次作业

我只列出了超过一定阈值的度量信息,即可能存在复杂度过高的情况

OO第一单元总结_第1张图片

OO第一单元总结_第2张图片

OO第一单元总结_第3张图片

第一次作业我本来是想尝试建立层次关系,以便后续扩展。因此所有可求导的类继承了求导接口,然后又按常规划分表达式层次的方式,用表达式、项、因子三层进行划分。这导致我第一次作业整体代码量很大,但到第三次作业的时候其实并没有派上太大用场,除求导接口外其他大部分都重构了。我的教训就是千万不要为了OO而OO,建立一些实际上用不着的层次关系,架构的设计还是要根据实际的需要。

复杂度分析中可以看到我的表达式类和多项式类的toString方法复杂度较高,这是因为我把优化输出的部分放在了toString部分,导致方法内有大量的条件判断,如判断指数是否为0为1等。如果要优化架构,可能会考虑彻底把优化输出的部分独立出来,或者把条件判断用私有的方法简化一下,可能会使单个方法的复杂度降低。另一个复杂度较高的方法是工厂中获取项的方法,这个是因为第一次的格式太简单,我有点偷懒,想用类似递归下降的方式去解析,结果发现一个方法内用条件判断和循环就搞定了,但是也导致这个方法比较复杂。类复杂度可以看出也是由于前面提到的这几个方法导致的。

第二次作业

OO第一单元总结_第4张图片

OO第一单元总结_第5张图片

OO第一单元总结_第6张图片

第二次作业基本延续了第一次的架构,同样地分层没有派上大用场,我还是把幂函数和三角函数以及系数用一个统一的类来表示,因为这样虽然不利于后续拓展,但对于化简很简单。由于架构没变化,只是加了三角函数,复杂度较高的方法和第一次没有太大区别。

第三次作业

OO第一单元总结_第7张图片

第三次作业大幅度重构了架构,基本上采用了指导书上的方式,但是也偷了一点懒,因为实际上只有三角函数用到了嵌套关系,我并没有额外增加一个表示嵌套的类,而是在三角函数类内部进行表示。

复杂度较高的方法只有递归下降解析三角函数时的方法,还是上面的原因,因为三角函数本身格式比较复杂,用到了大量的条件判断,条件可能也比较复杂,优化方式就是把一些复杂的条件判断独立为私有方法。

Bug分析

三次作业的强测和互测中我都没有被找到bug,前两次作业是因为本身难度不高,第三次是我放弃了优化输出的部分,尽量保持程序的简洁性,主要精力放在了保证正确性上。

寻找bug的策略

  • 覆盖性测试,第一次作业不少同学在某些项的输出时存在一些类似“笔误”的错误,后面的作业由于复杂度提升也有类似的低级错误,这些可以在写代码阶段通过单元测试等手段得到解决。
  • 压力测试,主要是第一次作业,用各种长表达式检测同一组内的正则表达式是否存在性能问题。
  • 边界测试,手工构造的一些边界点。

设计模式的应用

虽然在之前还看了一些设计模式相关的书,但非常遗憾这三次作业基本上没用到具体的设计模式。第一次作业虽然从名字上用了工厂,但只是用了一个静态方法形式的简单工厂,后两次作业解析输入用的是递归下降,创建对象的具体参数可以通过递归下降的过程方便地得到,似乎也没有用工厂模式的必要。至于其他的设计模式,可能是我理解不够,没有想到如何在这次作业中使用,希望在之后的作业中找到更多应用设计模式的机会吧。

你可能感兴趣的:(OO第一单元总结)