BUAA_OO 第一单元
BUAA_OO 第一单元
第一次作业
1 题目要求
读入一个包含加、减、乘、乘方以及括号(其中括号的深度至多为 1 层)的单变量表达式,输出恒等变形展开所有括号后的表达式。
2 架构
2.1预处理
通过预处理完成了
-
-
连续+-的删除
-
表达式的幂次展开,即将(x+1)**2替换为(x+1)*(x+1).这里是通过正则表达式进行的,但是对于之后两次作业,因为加入了三角函数等,不能再针对括号进行替换.
2.2分析字符串
在第一次作业中,Lexer类中peek方法返回的是常数因子 幂函数因子或单个字符,然后在Parser类中使用正则表达式进行不同类型因子的匹配,在这里使用正则表达式过多,显然并不利于整体代码的简洁和后续迭代.
2.3返回字符串
使用toString方法返回字符串,在Term类中,返回之前先进行项中每个因子的类型判断,相同类型的因子相乘,然后再将三个不同类因子相乘得到一个表达式结构,返回表达式的toString返回值.
2.4合并同类项
将得到的没有括号的字符串再次分析,得到多个符号,系数,指数的组合,相同的进行合并.这里使用了额外的类进行数据的存储,其实是不必要的.
3 UML图
优点:结构清晰
缺点:存在冗余内容
4 程序度量
4.1代码量
4.2类复杂度
Class | OCavg | OCmax | WMC |
---|---|---|---|
due.Lexer | 4.33 | 11 | 13 |
due.MainClass | 1 | 1 | 1 |
due.Merger | 3 | 6 | 9 |
due.Parser | 3 | 7 | 12 |
due.Pre | 2.8 | 6 | 14 |
expr.Expr | 1.38 | 3 | 11 |
expr.Item | 2.6 | 6 | 13 |
expr.Number | 1.17 | 2 | 7 |
expr.Power | 1.5 | 3 | 9 |
expr.Term | 3.8 | 7 | 19 |
4.3方法圈复杂度
Complexity metrics | 周四 | 24 3月 2022 20:11:20 CST | ||
---|---|---|---|---|
Method | CogC | ev(G) | iv(G) | v(G) |
due.Lexer.next() | 37 | 5 | 14 | 18 |
due.Merger.run() | 16 | 1 | 5 | 6 |
due.Parser.parserFactor() | 21 | 4 | 5 | 7 |
expr.Term.addFactor(Factor) | 16 | 1 | 6 | 7 |
expr.Term.toString() | 14 | 2 | 7 | 8 |
这里给出的是几个复杂度比较高的方法,Lexer.next()甚至达到37,几个方法中还存在很多可以简化的地方,还需要进一步修改.
5 bug分析
在第一次作业中出现的bug是equals和= =的使用问题,在对字符串进行比较时,使用了= =,从而导致出现了问题.
第二次作业
1 题目要求
读入一系列自定义函数的定义以及一个包含简单幂函数、简单三角函数、简单自定义函数调用以及求和函数的表达式,输出恒等变形展开所有括号后的表达式。
2 架构
2.1分析字符串
对比于第一次作业,在本次作业中,将使用正则表达式改为了输出字符和数字,在Parser类中进行分析构建因子的形式.
2.2表达式的幂次问题
由于三角函数 求和函数 自定义函数中存在括号,在预处理中直接展开括号的方式行不通了,所以这里在表达式项中添加了属性index存储幂次.在项添加因子的过程中,如果表达式因子的指数大于1,则计算表达式的乘法得到一个指数为1的表达式.
2.3求和函数和自定义函数
在项中并不存储求和函数和自定义函数,在分析表达式得到函数后直接利用Rep类进行替换,得到一个表达式,将表达式存储进项中.
2.4两个方法类
Mul类是从第一次作业中表达式层次中给个类中提取得到(本意是使代码结构更加清晰,所以单独建立了类,然而第三次作业又改回到了各个类中),相较于第一次作业的变动是,第一次作业的乘法返回的是字符串,而本次作业返回的是类,这样可以使得做完乘法运算后便于进行其他运算.
Rep类的作用是将给定表达式中的x y z i按需替换为因子,与Mul类的实现原理相同,都是采用递归下降的方式最终改变表达式.
3 UML 图
4 程序度量
4.1代码量
4.2类复杂度
Class | OCavg | OCmax | WMC |
---|---|---|---|
caler.Mul | 1.4 | 3 | 7 |
caler.Rep | 3.5 | 6 | 21 |
due.Custer | 1 | 1 | 5 |
due.Lexer | 3.5 | 9 | 14 |
due.MainClass | 3 | 5 | 6 |
due.Parser | 3.5 | 10 | 21 |
due.Pre | 2.67 | 6 | 8 |
expr.Cust | 3 | 9 | 12 |
expr.Expr | 1.14 | 2 | 8 |
expr.Number | 1.33 | 2 | 4 |
expr.Power | 1.4 | 3 | 7 |
expr.Sum | 2 | 3 | 4 |
expr.Term | 3.25 | 15 | 39 |
expr.Trig | 1.4 | 3 | 7 |
4.3方法圈复杂度
Complexity metrics | 周四 | 24 3月 2022 20:47:52 CST | ||
---|---|---|---|---|
Method | CogC | ev(G) | iv(G) | v(G) |
due.Lexer.next() | 11 | 2 | 6 | 10 |
due.Parser.parserFactor() | 18 | 9 | 17 | 17 |
expr.Cust.parserrExpr() | 15 | 1 | 9 | 9 |
expr.Term.addFactor(Factor) | 37 | 8 | 15 | 15 |
字符串解析方式进行更改后,可见Lexer.next()方法的复杂度由37降为11.但是本次作业中,由于将因子分析计算的过程放在Term.addFactor(Factor)方法中,导致此方法的复杂度过高.
5 bug分析
-
使用了replaceALL进行字符串的替换导致了问题
-
在对自定义函数引用进行替换时,三个参数的替换流程过于复杂,导致分析出现问题.
类:Cust
方法:parserrExpr()
特征:
多个if else 语句叠加,在最后一个else if后没有else结束,其实此处不在需要if,即需要将最后一个else if改为else.
Cust类的源码有76行,第三次作业修改后Cust源码仅有32行.
parserrExpr() 方法的CogC(认知复杂度)为15,远远超出绝大多数方法,而第三次作业中经过修改的parserrExpr()方法的CogC仅为3.
第三次作业
1 题目要求
读入一系列自定义函数的定义以及一个包含幂函数、三角函数、自定义函数调用以及求和函数的表达式,输出恒等变形展开所有括号后的表达式。
2 架构
2.1接口实现的改变
将Term类改为Factor接口的实现,将Cust和Sum类作为单独的类.这里是根据类的行为做出的改动,因为Number Power Trig Expr Term这5个类都需要实现乘法和替换的方法,所以将这两个方法作为接口Factor的方法.而Sum和Cust类并不需要实现这两个方法,所以将Cust和Sum类作为单独的类.同时将第二次作业中的两个工具类取消.
2.2 合并同类项
这里将Expr类中用ArrayList存储项,改为用HashMap存储项,key值是将项的系数改为1的新项,value值为项的内容,添加项时如果key值相同,只需将value内的系数相加.
3 UML图
4 程序度量
4.1代码量
4.2类复杂度
Class | OCavg | OCmax | WMC |
---|---|---|---|
due.Custer | 1 | 1 | 5 |
due.Lexer | 3.5 | 9 | 14 |
due.MainClass | 3 | 5 | 6 |
due.Parser | 3.5 | 10 | 21 |
due.Pre | 2.67 | 6 | 8 |
expr.Cust | 2 | 4 | 6 |
expr.Expr | 2 | 4 | 22 |
expr.Number | 1.29 | 3 | 9 |
expr.Power | 2.25 | 7 | 18 |
expr.Sum | 2 | 3 | 4 |
expr.Term | 2.69 | 10 | 43 |
expr.Trig | 1.56 | 4 | 14 |
4.3方法圈复杂度
Complexity metrics | 周四 | 24 3月 2022 21:28:35 CST | ||
---|---|---|---|---|
Method | CogC | ev(G) | iv(G) | v(G) |
expr.Cust.parserExpr() | 3 | 1 | 4 | 4 |
expr.Power.repFactor(String, Factor) | 9 | 7 | 6 | 7 |
due.Lexer.next() | 11 | 2 | 6 | 10 |
due.Parser.parserFactor() | 18 | 9 | 17 | 17 |
expr.Term.addFactor(Factor) | 23 | 5 | 9 | 10 |
由于类之间结构的变化,Term.addFactor(Factor)方法的CogC由37降为23,同时实现了应有的功能,可见一个更好的架构可以直接影响类和方法的简洁程度.
心得体会
不足:
-
面向过程的思想:在写代码的过程中,出现了if switch for while等的大量循环嵌套,导致复杂度提高,而且可能出现未被测试到的bug.
-
本单元中只在完成功能的基础上进行了同类项的合并,没有进行更深入化简.
-
结构设计的不合理会导致问题.在第一单元的作业中,虽然我没有进行大面积的重构,但是从反复修改中,也可以看出对面向对象编程层次结构搭建的不成熟才导致了代码层面上的不简洁.希望在之后的学习中能够不断弥补这方面的缺陷.
收获:
-
对递归下降更加熟练,表达式的分析,乘法运算和替代都是通过递归下降实现的.
-
在迭代过程中,逐步完善了代码的框架结构,同时增进了对面向对象框架构造的理解.