前言

  从接触OO课程至今,我已经完成了三次OO的关于表达式求导的作业,也是时候对自己的编程作业进行一次总结了。鉴于三次作业从指导书上描述差别不大,因此将主要针对第三次关于表达式求导的代码进行分析。

程序结构的分析

对程序架构的描述:

  对于OO(即面向对象编程),我目前为止一直采取的策略是尽可能地对目标进行封装,但鉴于一,二两次作业并没有多大地工程量,因此我并没有采取严格地分类,即输入类,输出类,sin类等的分类方式,第一次作业只有一个类,而第二次也只分了Expressioninput,ExpressionProcess,ItemProcess和Poly四个类,从类的命名可以看出,我分类的思路并不是对一个单纯的物体对象进行封装,我封装的对象是一个过程,这其实源于我对面向对象的理解。我认为的OO强调的是一个类只需要给与和给出信息,就算合格,但是很多时候,给出的信息也需要统合才能真正起到作用,就好像火箭的每个模块都造好了,但仍需要一条流水线来完成火箭的组装一样,而流水线本身也可以作为一个对象进行封装,这就是我前两次作业的架构思想。而第三次作业随着难度的增加,我在封装流水线的基础上进一步细化,如下图。

  从上图,我细化了封装的求导部分,拆解成单独的加法式(包含在Inputcheck类里),乘法式,括号式,以及各类一般因子的处理,总体上对类分划分个人还是比较满意,但问题出在加法式处理这一部分,从上图(Statics插件分析的结果)看,很明显Inputcheck这一负责处理输入格式问题的类行数远多于其它类,造成这一现象的原因是,我把加法式类写在了这个类里面,正确的做法应该是采用继承,或类的声明与调用。此外,加法式类完全与其他类脱节,导致其内含有大量的杂碎功能,包括但不限于格式的检验,括号的匹配,这样的设计是完全违背封装的初衷的。

经典的OO度量

  采用了IDEA的插件Metricsreloaded对第三次作业计算经典OO度量,结果如下图:

  从上图,可以看出我在第一部分对Inputcheck的分析是完全正确的,Inputcheck内这几个重要方法,基本复杂度高,模块设计复杂,与其他模块的耦合度高,而且难于独立地进行检查和维护,可以说相当的失败。而各类的方法普遍具有结构化较差的缺点,据我分析,原因是我没有采用继承,由父节点负责相同的方法,而是对每个类似的类都手动copy了一个类似的方法,模块化低下,难以维护。

程序类图

 

   优点:

    1. 基本对不同对象进行了彻底封装。
    2. 实现了通过不同类之间的调用完成降层的求导过程
    3. 采用继承,实现了常量的多次复用

   缺点:

    1. 没有完全正确地设计类的种类,例如加法式类被写成了Inputcheck的一个方法
    2. 继承没有正确运用,其价值不仅在继承常量,还在方法的复用
    3. 方法没有正确构建,方法之间存在重复

分析BUG

  目前为止的三次作业,第一次在强测和互测环节均未出现BUG,下面以第二次和第三次做分析。

  • 第二次作业

  输入示例:

 

x*sin(x)*cos(x)-+-2*x^-3*sin(x)^-2*cos(x)^+3

 

  我的错误输出:

x*cos(x)^2-x*sin(x)^2+sin(x)*cos(x)-6*x^-3*sin(x)^-cos(x)^2-4*x^-3*sin(x)^-3*cos(x)^4-6*x^-4*sin(x)^-2*cos(x)^3

  很明显,我的错误是输出地部分内容莫名其妙地消失了,在BUG修复阶段我重新审查了程序,发现错误在这里

                temp = temp.replaceFirst("-1\\*","-");

  这段代码本意是将每一项开始系数为“-1*”的部分省略为“-”,但是,在实际操作过程中它的实际效果是将第一个匹配到的“-1*”省略为“-”,修改为如下即可,出现这个问题主要是没有很好的明白获得信息和要求的信息具体条件是什么。

                temp = temp.replaceFirst("^-1\\*","-");

 

  • 第三次作业

 

  1. 输入示例: 
cos(    (-sin    (((0)+0))))*    sin(100000000)    +    cos    ((x*    00000)    )

   2. 输入示例:  

cos((cos(sin(x^3)^3)^3))^2*x^1*sin(x^2)^1

  以上输入我的程序都会给出”WRONG FORMAT!“的错误输出,细察其原因,问题出在Inputcheck类中的special方法上,该方法设计的初衷是为了全覆盖检测"sin()"、"cos()"内的括号内容是否合法,但在实际设计的时候并没有将所有的可能涵盖在内,属于设计不全面造成的BUG。勉强可以和设计的重复性导致的设计思路混乱扯上关系,不过,归根结底,应该是设计的不完善造成的。

  为了解决这个BUG产生的根源,应做好完善的设计档案,至少,针对需要完备性设计的程序块要有一个清晰的设计,以便审核全覆盖与否。

分析别人BUG的策略

  1. 使用自己的测试集测试;
  2. 分析指导书,进一步寻找可能的边界条件,构建测试样例测试
  3. 对代码进行分析,针对性写测试样例

 总结和反思

  我的第一次、第二次和第三次作业都是完全独立的,即没有采用重构的方式,主要原因还是第一次和第二次重构的代价太大。第一次作业完全只有一个类,不具备重构的价值;第二次作业与其说是面向对象的思路,不如说是面向流水线的拼接思路,本质上走的还是顺序编程的思路,同样没有重构的价值。第三次作业,将分别封装独特的类,已经具备了重构和继承的基础,在第三次作业的基础上可以继续添加条件和要求。对针对性的类,比如对乘法的拆解,完全可以采用继承的方式,添加需要增加的因子,或者采用重构,更改拆解的方式和格式。但是,因为加法式的拆解内嵌在了Inputcheck里面,对它的重构可能会出现不可预料的结果,因此,如果不更改Inputcheck的结构,重铸加法式类,那么最好采用继承的方式重写。