前言

  对于像我这样的第一次接触面向对象编程的人来说,在写第一次作业的时候我就一直在思考,我的程序怎么样才能写的有面向对象思维,在翻阅了许多书籍以后,我也只是粗略地有个模糊印象,比如每个类里面的方法需要尽量地减少对其他类方法的依赖,做到尽量地独立。在分析问题的时候要先考虑问题中的元素能够抽象成什么样的对象,比如第三次作业设计的时候,我在看完指导书就有了一个架构的设计:通过表达式->项->因子->表达式构建一个树形结构来进行链式求导,因此对于求导问题就可以抽象为表达式、项、因子之间的交互,最终通过它们各自的求导规则来完成所要求的求导服务。因子又可以作为一个父类,三角函数、幂函数和常数作为它们的子类,对构造器和方法进行重写,再次使用它们各自的求导规则进行求导,通过第三次作业我也渐渐明白了面向对象的思想,希望能在下一个单元的学习中有更多的收获。

 

1.需求分析

  本次作业总体目标是构建一个能判断输入是否合法、并且能对合法的输入表达式进行求导的程序。如果输入不合法,则输出WRONG FORMAT! 如果输入合法,则应当求出正确答案。

 

2.程序设计思路与结构分析

  这部分我采用了IDEA的Metrics插件的Complexity mertics,对于这个插件有一些术语在这里需要解释一下:

  • ev(G): 一个方法或者类的结构化复杂度,值越高复杂度越高。
  • iv(G): 一个方法及其调用的其他方法的紧密程度,值越高则紧密程度越高。
  • v(G): 一个方法或类的循环复杂度,值越高则循环复杂度越高。在类中,有OCavgWMC两个指标,分别代表类的方法的平均循环复杂度和总循环复杂度。

 

   2.1:第一次作业

  • 思路:

    第一次作业只要求了简单多项式的求导,因此思路也很简单,只需使用公式即可。

  • 输入处理

    一开始的时候本人采用的是大正则匹配法,即使用一个正则表达式来对所有合法情况进行处理,这样带来的问题是会导致输入字符串过长的时候stackOverflow,所以后来我就采用了项匹配法:

1      String next = "[ \\t]*[+-][ \\t]*[+-]?" + "("
2                     + "([ \\t]*x([ \\t]*\\^[ \\t]*[+-]?\\d+)?" +
3                     "|\\d+[ \\t]*\\*[ \\t]*x([ \\t]*\\^[ \\t]*[+-]?\\d+)?"
4                     + "|\\d+)[ \\t]*)";//一项的正则表达式

      每找到一项以后,再匹配下一项:

 1 while (m.find()) {
 2                 start = m.start();
 3                 if (start != end) { //如果这次开始和上次结束的位置不一致,则匹配失败
 4                     System.out.print("WRONG FORMAT!");
 5                     return;
 6                 }
 7                 end = m.end();
 8             }
 9             if (end != input.length()) {
10                 System.out.print("WRONG FORMAT!");
11                 return;
12             }

    这样就成功地化整为零,避免了爆栈。

  • 求导函数设计思路

    程序设计思路较为简单,只需采用公式法对每一项进行求导即可,为此构建一个Poly类进行相应的操作,具体架构见第三部分。

  • 程序结构分析

    整体结构如下图:背景为壁纸

    面向对象程序设计第一单元总结(求导程序)

      Main为主函数,调用Poly类函数的方法进行求导与输出,derivate为求导方法,combine为合并同类项方法,output为输出表达式方法,isnature为检查是否为正数方法。而outout调用combine,combine调用derivate,main调用output,这次作业还是有很多面向过程的思维存在。

  • 复杂度分析:(class\method)

 

    面向对象程序设计第一单元总结(求导程序)           

                             

        面向对象程序设计第一单元总结(求导程序)

    可以看出,本次作业中我各个类的循环复杂度都较高,因为我用的是Arraylist,所以查找效率较低,建议可以采用Hashmap。而方法的结构复杂度较低,有少数几个方法的紧密度>=10而标为了红色,这是因为多次调用了isnature方法来检查是否为正数;循环复杂度还是由于采用实现简单但效率低下的Arraylist结构,导致了循环复杂度较高。

  • 总结:

    第一次作业还是有较多的面向过程思维的存在,可能题目本身就适合面向过程写吧并且为了贪图实现的方便采用了Arraylist结构,没有使用效率更高的Hashmap,其次本次优化也很简单,只需正项提前即可。

 

    2.2:第二次作业

  • 思路

    第二次作业相对于第一次作业更加复杂了一点,除了引入三角函数之外,还需要支持因子的连乘,但变化不是很大,只需给每一项设置4个属性:常数、幂函数指数、三角函数(sin、cos)指数即可,而对于a*xc*sin(x)d*cos(x)e,是有确定的导函数的,可以通过公式来实现求导。

  • 输入处理    

    同样采用上一次作业的项匹配法,匹配到一个项的时候,就将其作为term的一个对象存入。这里就不再重复展示代码。

  • 求导函数设计

    和第一次作业一样,采用求导公式:

 1     Poly output(Poly temp, String s) {
 2         init(getTerm(s));
 3         if (op == '-') {
 4             num[0] = num[0].multiply(BigInteger.valueOf(-1));
 5         }
 6         temp.addItems(temp, num[1].multiply(num[0]),
 7                 num[1].subtract(BigInteger.ONE), num[2], num[3]);
 8         temp.addItems(temp, num[2].multiply(num[0]),
 9                 num[1], num[2].subtract(BigInteger.ONE),
10                 num[3].add(BigInteger.ONE));
11         temp.addItems(temp,
12                 num[3].multiply(num[0]).multiply(BigInteger.valueOf(-1)),
13                 num[1], num[2].add(BigInteger.ONE),
14                 num[3].subtract(BigInteger.ONE));
15         return temp;
16     }

    看起来很复杂,只是普通的求导函数的写法而已。

  • 程序结构分析

   整体结构如下图: 

    面向对象程序设计第一单元总结(求导程序)

    main函数调用Poly的output方法进行输出,Poly类构造term的对象,并调用term中的求导函数完成求导以后的项,存到Poly中,最后通过三次合并(第一次合并同类项,第二、第三次进行三角恒等变换),输出最终结果。

  • 程序复杂度分析

    类和方法的复杂度如下:

    面向对象程序设计第一单元总结(求导程序)

 

面向对象程序设计第一单元总结(求导程序)

    可以看出各个类的循环复杂度都较高,因为在合并同类项的时候有三次遍历搜索,所以效率非常低,而各个方法中也能看出,输出部分和合并同类项部分的循环复杂度和与其他方法的紧密很高,因为都有一个遍历的过程,并且在合并同类项的时候频繁调用了一些判断是否相差2(ifLessTwo)、减2(subTwo)、删除项等方法。好在其他的方法依赖度并不高,结构也控制的较好。

  • 总结

    本次作业中我开始有了一些面向对象思维的展现,把表达式、项各当成了一个对象,而因子可以归为项的一个属性,因此也就没有增加因子类,这次的设计中除了在优化过程中的循环复杂度较高外,依赖度和结构复杂度都有了一个较好的控制。

 

  2.3 第三次作业

  • 思路

    第三次作业和第二次作业有了难度较大的提升,表达式支持嵌套、允许有括号的存在使得我前两次作业的思路完全无法参考,但是在看指导书的过程中,表达式->项->因子->表达式...这个树形结构的存在,使我有了构造表达式树的想法,并且这次作业完全可以抽象为多个不同的类,完全可以使用面向对象的思维进行解答。

  • 输入处理

    我的输入处理较为复杂,在使用正则表达式匹配的过程中,我对于表达式因子采用了 \\(.+\\)这种匹配方法,但是遇到(x)+(x)的时候就会匹配成(x)+(x)整个项,这显然是不符合要求的,因此我采用了栈的方式手动对其进行纠正。

    因子的正则表达式:

1 private static final String factor =
2             "[ \\t]*(((sin[ \\t]*\\(.+\\)" +
3                     "|(cos[ \\t]*\\(.+\\))" +
4                     "|x[ \\t]*)([ \\t]*\\^[ \\t]*[+-]?\\d+)?)" +
5                     "|[+-]?\\d+|\\(.+\\))[ \\t]*";

    

    对正则表达式的匹配纠正(较长,因此折叠)

 1 private void makeTerm() {
 2         try {
 3             String term = "";
 4             StringBuilder temp = new StringBuilder(input);
 5             int start = 0;
 6             int end;
 7             int stack = 0;
 8             char s;
 9             char lastchar = '\0';
10             boolean flag = false;
11             for (int j = 0; j < input.length(); j++) {
12                 s = input.charAt(j);
13                 if (j == input.length() - 1) {
14                     term = temp.substring(start, j + 1);
15                     if (term.matches(pattern)) {
16                         Term newTerm = new Term(term);
17                         childs.add(newTerm);
18                     } else {
19                         throw new FormatException();
20                     }
21                 }//读到行末,结束
22                 if (String.valueOf(s).matches("\\s")) {
23                     continue;
24                 }//跳过空格
25                 if (s == '(') {
26                     stack++;
27                 } else if (s == ')') {
28                     stack--;
29                 }
30                 if (flag && (s == '+' || s == '-')
31                         && (lastchar == '*' || lastchar == '^')) {
32                     continue;
33                 }//跳过'*'和'^'后面的'+'和'-'
34 
35                 if (s != '+' && s != '-' && !flag) {
36                     flag = true;
37                 }//跳过前导加号
38 
39                 if (stack == 0 && (s == '+' || s == '-')) {
40                     if (flag) {
41                         end = j;
42                         term = temp.subSequence(start, end).toString();
43                         start = j;
44                         if (term.matches(pattern)) {
45                             Term newTerm = new Term(term);
46                             childs.add(newTerm);
47                             stack = 0;
48                             flag = false;
49                         } else {
50                             throw new FormatException();
51                         }
52                     }
53                 }//满足条件则存入
54                 lastchar = s;
55             }
View Code

相关文章:

  • 2021-11-02
  • 2021-11-18
  • 2021-12-07
  • 2022-02-05
  • 2021-12-04
  • 2021-09-04
  • 2021-11-30
猜你喜欢
  • 2022-12-23
  • 2021-05-27
  • 2021-09-18
  • 2021-06-29
  • 2021-11-24
  • 2021-10-20
  • 2021-12-10
相关资源
相似解决方案