一、基于度量分析
- OCavg:类的方法的平均循环复杂度
- OCmax:类的方法的最大循环复杂度
- WNC:类的方法的总循环复杂度
- ev(G):即Essentail Complexity,用来表示一个方法的结构化程度,范围在[1,v(G)]之间,值越大则程序的结构越“病态”,其计算过程和图的“缩点”有关。
- iv(G):即Design Complexity,用来表示一个方法和他所调用的其他方法的紧密程度,范围也在[1,v(G)]之间,值越大联系越紧密。
- v(G):即循环复杂度,可以理解为穷尽程序流程每一条路径所需要的试验次数。
1.第一次作业
- 第一次作业有点假面向对象的感觉,其实还是完全没走出面向过程的思维。
- 直接把每一项表示成由常数和次数就可以,求导也可以直接改变常数和次数。
类图
- Poly表示项,由系数和次数组成。
- 整体思路很清晰,很直接。
- 在Poly类中完成了求导操作,但转化为字符串输出却没有整合进类中,这是可以改进的地方。
复杂度
- 可以看到主类的循环复杂度很高,主要是还没走出面向过程的思路,也不太会使用Java的一些数据结构,一直在造轮子
- 主函数的复杂度相当高,并且转化为字符串输出的函数也很复杂,主要是通过循环来实现的。这是第一次作业最大的问题。
2.第二、三次作业
- 第二、三次作业的代码非常相似,因此只分析第三次的代码。
- 整体思路是按照树形结构分割,一步一步递归下去直接完成求导。第三次在第二次基础上加了个WF就过了。
类图
- 主类中有大量函数是用来判断WF的,事后发现应该将这些重新封装到一个类中。这十几个函数差点导致主类代码超过500行,问题很大。
- Expression对应题目中的“表达式”,Item类是一个综合体,对应了题目中定义的“项”,Sin类、Cos类、Poly类则对应各种因子。
复杂度
- 好几个类的循环复杂度相当高,还是没摆脱面向过程,并且对Java不熟练,总是不由自主的造轮子。
- 主类的复杂度很夸张,一部分原因是有关字符串处理的代码在主类中,另一部分原因是对WF判断的代码也在主类中。似乎本应该把他们单独在外处理的。
- 各部分处理还算挺清晰,但解耦的处理做的很不好,应对第三次作业似乎勉强足够。以后需要进一步在解耦上花时间,多构思构思。
二、分析自己程序的bug
1.第一次作业
- 无
2.第二次作业
- 出现对连续三个符号处理不当的问题。
- 在递归过程中“额外添加0”导致的递归层数过多而运行速度慢或爆栈。
- 递归时判断顺序从左到右导致运算顺序改变因而发生错误。
3.第三次作业
- 对sin(0)、cos(0)未特殊处理导致运行时异常。
总结
- 出现bug的方法代码行数往往较多,圈复杂度也较高,如求导方法、输出方法等。
- 今后要尽可能降低单个方法的行数和圈复杂度,这样对减少bug应该会有帮助。
三、分析自己发现别人程序bug所采用的策略
1.第一次作业
- 由于逻辑较为加单,代码量小,所以直接看别人代码来分析是否存在逻辑问题。
2.第二次作业
- 测试有关递归嵌套的问题;
- 较多符号叠加的问题;
- 较多项的复杂情况。
3.第三次作业
- 测试三角函数括号中嵌套的情况;
- 其余与第二次作业大致相同。
四、重构经历总结
- 第一周写的算法几乎没有用到面向对象的思想,直接导致第二周作业发布时立刻决定重构。
- 在第二周时吸取了上一周的教训,提前为可能到来的三角函数嵌套和表达式嵌套(后来没来)做出了准备,因此第三次作业比较轻松。
五、心得体会
- 架构的可扩展性很重要,可扩展性低往往会导致增加或改变功能时大规模重构;要将面向对象的思想融入自己的架构中,要一边写代码一边默念“面向对象”。
- 第三周作业的格式判断有点奇怪,没想明白和面向对象的关系在哪;并且也并非如作业提示所说“推荐使用正则表达式”,事实上从各方面来讲正则表达式都不是个好选择,让人走了不少弯路,所以还是得勤看讨论区,不然都不知道还有递归下降等更好的思路。
- 互测很多同学那叫一个不遗余力,可后来感觉似乎也没几分,影响不大,第一、二周花较多时间去hack别人似乎不太值得(别人总能一个合并修复给你全部修好)。
- 正确性的重要性远大于优化,错一个点那就是真错了。花太多时间去整性能分实非智者之选,不如好好自测找找bug。