BUAA OO 第一单元总结
第一次作业
本次作业总体来说比较简单,但考虑到后面作业的影响,尽可能采用了面向对象的方法(虽然还是重构了……),并尽可能进行了一定程度上的优化。
架构介绍
数据存储
本次作业主要设置了Integral, Polynomial, VariableX, Dericatives四个类对数据进行存储,它们之间的关系符合下面的规定:
VariableX类存储幂函数,仅含有指数,格式为 V = x ** index。
Derivative类存储幂函数的导数,即存储VariableX求导之后的结果,格式为 sign coefficient * V。
Integral类存储整式,即表达式,其中的内容为Polynomial类的哈希图,以每一项的指数值为哈希值,对指数相同的项进行合并,存储一系列的项,符合规定 I = P1 + P2 + ...。
辅助类
除了数据存储使用的类,本次作业还使用了StringProcess类,FactoryImpl类进行辅助作业。
StringProcess类是字符串分析类,本次作业采用了大正则表达式,按照指导书进行迭代,并获取相应的信息,传给工厂类进行处理。
FactoryImpl类为工厂类,对StringProcess类传来的信息进行处理,并生成各种数据存储类,进行数据储存。
类关系图
数据处理方法
由于本次作业相对比较简单,数据处理相对比较容易。
首先将获取的字符串传入StringProcess类进行处理,之后分析得到的信息交给FactoryImpl类生成各种数据存储类。之后将Polynomial类中的VariableX类、常数以及符号进行合并,得到统一的指数、系数和符号,之后再根据Polynomial类中的指数将Integral类中的Polynomial类进行合并。化简结束后,对每一个VariableX进行求导操作得到Derivative类,并根据原始VariableX类所在的Polynomial类生成新的Polynomial类(求导结果项),组成最终的求导结果并输出。
度量分析
方法复杂度分析
| method | CogC | ev(G) | iv(G) | v(G) |
|---|---|---|---|---|
| Derivatives.Derivatives() | 0.0 | 1.0 | 1.0 | 1.0 |
| Derivatives.Derivatives(VariableX) | 2.0 | 1.0 | 2.0 | 2.0 |
| Derivatives.getCoefficient() | 0.0 | 1.0 | 1.0 | 1.0 |
| Derivatives.getVariableX() | 0.0 | 1.0 | 1.0 | 1.0 |
| Derivatives.isSign() | 0.0 | 1.0 | 1.0 | 1.0 |
| Factory.getFactory() | 0.0 | 1.0 | 1.0 | 1.0 |
| FactoryImpl.createDerivatives(VariableX) | 0.0 | 1.0 | 1.0 | 1.0 |
| FactoryImpl.createIntegral() | 0.0 | 1.0 | 1.0 | 1.0 |
| FactoryImpl.createPolynomial(Derivatives,BigInteger,boolean) | 0.0 | 1.0 | 1.0 | 1.0 |
| FactoryImpl.createStringProcess() | 0.0 | 1.0 | 1.0 | 1.0 |
| FactoryImpl.createVariableX(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| Integral.getAPolynomial(BigInteger,BigInteger,boolean) | 8.0 | 1.0 | 4.0 | 4.0 |
| Integral.Integral() | 0.0 | 1.0 | 1.0 | 1.0 |
| Integral.printPolynomial() | 3.0 | 1.0 | 3.0 | 3.0 |
| MainClass.main(String[]) | 0.0 | 1.0 | 1.0 | 1.0 |
| Polynomial.getCoefficient() | 0.0 | 1.0 | 1.0 | 1.0 |
| Polynomial.isSign() | 0.0 | 1.0 | 1.0 | 1.0 |
| Polynomial.Polynomial() | 0.0 | 1.0 | 1.0 | 1.0 |
| Polynomial.Polynomial(BigInteger,BigInteger,boolean) | 0.0 | 1.0 | 1.0 | 1.0 |
| Polynomial.Polynomial(Derivatives,BigInteger,boolean) | 0.0 | 1.0 | 1.0 | 1.0 |
| Polynomial.printPoly(boolean) | 8.0 | 2.0 | 6.0 | 6.0 |
| Polynomial.setCoefficient(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| Polynomial.setSign(boolean) | 0.0 | 1.0 | 1.0 | 1.0 |
| StringProcess.stringCheck(String) | 1.0 | 2.0 | 1.0 | 2.0 |
| StringProcess.StringProcess() | 0.0 | 1.0 | 1.0 | 1.0 |
| StringProcess.stringProcess(String,Integral) | 14.0 | 1.0 | 7.0 | 9.0 |
| TestClass.main(String[]) | 1.0 | 1.0 | 2.0 | 2.0 |
| VariableX.getIndex() | 0.0 | 1.0 | 1.0 | 1.0 |
| VariableX.setIndex(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| VariableX.VariableX() | 0.0 | 1.0 | 1.0 | 1.0 |
| VariableX.VariableX(BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| Total | 37.0 | 33.0 | 49.0 | 52.0 |
| Average | 1.19 | 1.06 | 1.58 | 1.68 |
总体来看,代码各种复杂度分布比较均匀,且绝大部分都不高,代码的可读性和可维护性均比较高。
但是StringProcess.stringProcess(String,Integral)这一方法的圈复杂度和认知复杂度均相对较高,造成这一结果的原因是字符串处理时并没有进行标准化而是直接进行处理,因此需要进行一系列的特判,导致复杂的过高。
类复杂度分析
| class | OCavg | OCmax | WMC |
|---|---|---|---|
| Derivatives | 1.2 | 2.0 | 6.0 |
| FactoryImpl | 1.0 | 1.0 | 5.0 |
| Integral | 2.67 | 4.0 | 8.0 |
| MainClass | 1.0 | 1.0 | 1.0 |
| Polynomial | 1.62 | 6.0 | 13.0 |
| StringProcess | 3.33 | 7.0 | 10.0 |
| TestClass | 2.0 | 2.0 | 2.0 |
| VariableX | 1.0 | 1.0 | 4.0 |
| Total | 49.0 | ||
| Average | 1.63 | 3.0 | 6.13 |
StringProcess类的平均方法复杂度相对较高,原因是对不标准的字符串进行处理需要特殊判断过多,导致代码臃肿。
复杂度评价
由于此次作业相对比较简单,总体来说,代码符合高内聚,低耦合的特点,并且具有一定的可扩展性。
测评
强测与互测均为同质bug,可简化为2*x-3*x样例出错,原因是由于Integral类中合并同类项时,对系数符号不同的项进行合并时,在确定符号之前就将系数更改为最终结果的绝对值,导致符号永远为正号。
互测中寻找其他人的bug采用的是黑箱测试的方法,通过手动构造数据,一般是自己认为自己可能会出错或者写代码过程中曾出过错误的数据进行测试,成功hack同一个测评房间的代码。
第二次作业
本次作业由于新添加了表达式因子,导致第一次作业的架构无法完成嵌套,因此重构。此次作业进一步考虑了第三次作业可能会遇到的情况,尽可能针对第三次作业的情况进行了架构设计。
架构介绍
数据存储
本次作业的数据存储主要依赖Formula, Term, Poly, Power, Sin, Cos, Constant七个类进行存储,符合以下规定:
Formula为父类,仅含有符号,其它六个类均继承自Formula类。
Constant类为常数类,存储常数因子。
Power类为幂函数类,存储幂函数的指数,格式为 P = x ** index。
Sin类为正弦函数类,存储正弦函数的指数,格式为 Sin = sin(x) ** index。
Cos类为余弦函数类,存储余弦函数的指数,格式为 Cos = cos(x) ** index。
Poly类为项类(还是英文意思搞混了……),存储一个ArrayList,里面为因子,存储的数据类型声明为Formula,实际可为其它五种类型,格式为 P = U1 * U2 * ...,其中U = Constant | Power | Sin | Cos | Term。
Term类为表达式类,存储一个ArrayList,里面为项,存储的数据类型声明为Formula,实际为Poly类,格式为 T = P1 + P2 + ...。
辅助类
本次作业采用字符串处理类、工厂类进行辅助。
Process类为字符串处理类,其功能为将获取的不标准的字符串格式化,得到无空白字符,无多余符号的表达式。
ExpressionFactory类为工厂类,其功能为从获取的标准字符串中提取所需要的信息,并生成相应的数据存储类。
类关系图

数据处理方法
本次作业引入了较为复杂的表达式因子,因此数据处理相对比较复杂,按照以下流程进行:
1.对传入的字符串进行格式化,并将格式化后的字符串交给工厂类。
2.工厂类按照表达式的生成方法进行字符串分析,将在括号外的两个'+'或'-'之间的字符串传递给项构造方法,并将构造完成的项组成一个ArrayList存储到Term类中。
3.对每一个项构造过程,对传入的字符串进行分析,将在括号外的两个'*'(注意要求'*'前后一个字符不可为'*',即不能构成'**')之间的字符串传递给因子构造方法,并将构造完成的因子组成一个ArrayList存储到Poly类中。
4.对每一个因子构造方法,对传入的字符串进行分析,依次进行表达式、正弦函数、余弦函数、幂函数、常数类型的判断,确定类型后存储至相应的类中,若为表达式因子,则递归调用表达式构造方法。
5.调用最外层的Term类的Derivative方法,递归调用所有数据存储类的Derivative方法,生成并输出最终结果。
度量分析
方法复杂度分析
| method | CogC | ev(G) | iv(G) | v(G) |
|---|---|---|---|---|
| Constant.Constant() | 0.0 | 1.0 | 1.0 | 1.0 |
| Constant.Constant(boolean,BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| Constant.derivative() | 0.0 | 1.0 | 1.0 | 1.0 |
| Cos.Cos() | 0.0 | 1.0 | 1.0 | 1.0 |
| Cos.Cos(boolean,BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| ExpressionFactory.creatProcess() | 0.0 | 1.0 | 1.0 | 1.0 |
| ExpressionFactory.creatScanner() | 0.0 | 1.0 | 1.0 | 1.0 |
| Factory.getFactory() | 0.0 | 1.0 | 1.0 | 1.0 |
| Formula.Formula() | 0.0 | 1.0 | 1.0 | 1.0 |
| Formula.Formula(boolean) | 0.0 | 1.0 | 1.0 | 1.0 |
| Formula.derivative() | 0.0 | 1.0 | 1.0 | 1.0 |
| Formula.isSign() | 0.0 | 1.0 | 1.0 | 1.0 |
| Formula.origin() | 0.0 | 1.0 | 1.0 | 1.0 |
| MainClass.main(String[]) | 0.0 | 1.0 | 1.0 | 1.0 |
| Poly.Poly() | 0.0 | 1.0 | 1.0 | 1.0 |
| Poly.Poly(boolean,ArrayList) | 0.0 | 1.0 | 1.0 | 1.0 |
| Power.Power() | 0.0 | 1.0 | 1.0 | 1.0 |
| Power.Power(boolean,BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| Sin.Sin() | 0.0 | 1.0 | 1.0 | 1.0 |
| Sin.Sin(boolean,BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| Term.Term() | 0.0 | 1.0 | 1.0 | 1.0 |
| Term.Term(boolean,ArrayList) | 0.0 | 1.0 | 1.0 | 1.0 |
| Process.formatStr(String) | 1.0 | 1.0 | 2.0 | 2.0 |
| Process.printAns(Formula) | 1.0 | 1.0 | 2.0 | 2.0 |
| Constant.origin() | 2.0 | 2.0 | 1.0 | 3.0 |
| Cos.origin() | 3.0 | 3.0 | 2.0 | 3.0 |
| Power.origin() | 3.0 | 3.0 | 2.0 | 3.0 |
| Sin.origin() | 3.0 | 3.0 | 2.0 | 3.0 |
| Cos.derivative() | 4.0 | 4.0 | 4.0 | 4.0 |
| Power.derivative() | 4.0 | 4.0 | 4.0 | 4.0 |
| Sin.derivative() | 4.0 | 4.0 | 4.0 | 4.0 |
| Term.derivative() | 4.0 | 2.0 | 2.0 | 5.0 |
| Term.origin() | 4.0 | 2.0 | 2.0 | 5.0 |
| ExpressionFactory.creatPoly(String) | 8.0 | 1.0 | 9.0 | 10.0 |
| Poly.origin() | 8.0 | 3.0 | 2.0 | 5.0 |
| ExpressionFactory.isExpressionUnit(String) | 15.0 | 7.0 | 6.0 | 10.0 |
| ExpressionFactory.creatTerm(boolean,String) | 20.0 | 7.0 | 10.0 | 13.0 |
| ExpressionFactory.creatUnit(String) | 20.0 | 9.0 | 10.0 | 10.0 |
| Poly.derivative() | 22.0 | 6.0 | 4.0 | 8.0 |
| Total | 126.0 | 84.0 | 90.0 | 116.0 |
| Average | 3.23 | 2.15 | 2.31 | 2.97 |
总体来看,数据存储部分的复杂度比较均匀且都不是很高,主要部分的可读性和可维护性比较好。
代码的主要问题在工厂类,工厂类方法的复杂度普遍较高,主要原因在于字符串分析和处理过程中需要进行一系列的复杂操作,而这一部分的工作均放置在一个方法里,且在数据存储过程中存在递归调用,因此会导致耦合度提高,可维护性下降。同时,在修复bug过程中对某一特殊类型的数据进行了优化处理,因此出现了特判情况,复杂度提高。
类复杂度分析
| class | OCavg | OCmax | WMC |
|---|---|---|---|
| Constant | 1.5 | 3.0 | 6.0 |
| Cos | 2.25 | 4.0 | 9.0 |
| ExpressionFactory | 5.83 | 10.0 | 35.0 |
| Formula | 1.0 | 1.0 | 5.0 |
| MainClass | 1.0 | 1.0 | 1.0 |
| Poly | 3.75 | 8.0 | 15.0 |
| Power | 2.25 | 4.0 | 9.0 |
| Process | 2.0 | 2.0 | 4.0 |
| Sin | 2.25 | 4.0 | 9.0 |
| Term | 3.0 | 5.0 | 12.0 |
| Total | 105.0 | ||
| Average | 2.76 | 4.2 | 10.5 |
通过类复杂度分析,可以验证上面的结论,复杂度主要在工厂类较高,因此可以考虑简化或分离字符串处理和存储模块生成的部分。
同时,Poly类复杂度也相对较高,这是由于Poly类下面涉及多种因子,需要对其进行不同的处理,导致复杂度的提高。
复杂度评价
总体来看,数据存储部分满足高内聚,低耦合的要求,但是在工厂类部分有着进一步优化的空间。
测评
本次作业强测与互测的问题均在于递归调用次数过多而导致的超时,问题数据可简化为(((((((((((((((((((x))))))))))))))))))),为解决这一问题,主要对工厂类进行了修改,对表达式中仅含有表达式因子的特殊情况进行了去括号的操作,减少了递归调用的次数,提高了速度,但是也导致了复杂度和耦合度的提高。
互测同样采用黑箱测试的方法,手动构造测试数据,采用自己曾出现问题的数据,并采用一些三角恒等变换的样例进行测试,成功hack。
优化思路
个人认为可以尝试在表达式格式化后对标准字符串处理,构造表达式树,生成去括号后的标准字符串并进行同类项的合并,再进行数据存储和求导操作,可以有效规避递归调用以及超时、爆栈的问题,也可为后续的优化操作做好准备。
第三次作业
由于第二次作业尽可能考虑第三次作业中可能出现的情况,此次作业直接套用第二次作业的架构,在第二次作业的基础上进行增量式设计,主要工作量在于格式检查方面。
架构介绍
数据存储
本次作业的数据存储主要依赖Formula, Term, Poly, Power, Sin, Cos, Constant七个类进行存储,符合以下规定:
Formula为父类,仅含有符号,其它六个类均继承自Formula类。
Constant类为常数类,存储常数因子。
Power类为幂函数类,存储幂函数的指数,格式为 P = x ** index。
Sin类为正弦函数类,存储正弦函数的指数以及函数内嵌套的因子,格式为 Sin = sin(U) ** index,其中U = Constant | Power | Sin | Cos | Term。
Cos类为余弦函数类,存储余弦函数的指数以及函数内嵌套的因子,格式为 Cos = cos(U) ** index,其中U = Constant | Power | Sin | Cos | Term。
Poly类为项类(同上,还是英文意思搞混了……),存储一个ArrayList,里面为因子,存储的数据类型声明为Formula,实际可为其它五种类型,格式为 P = U1 * U2 * ...,其中U = Constant | Power | Sin | Cos | Term。
Term类为表达式类,存储一个ArrayList,里面为项,存储的数据类型声明为Formula,实际为Poly类,格式为 T = P1 + P2 + ...。
辅助类
本次作业采用字符串检查类、字符串处理类、工厂类进行辅助。
Checker类为表达式字符串检查类,其功能为检查以表达式为单位的字符串格式。
UnitChecker类为因子字符串检查类,其功能为检查以因子为单位的字符串格式,即三角函数内嵌套的因子字符串。
Process类为字符串处理类,其功能为将获取的不标准的字符串格式化,得到无空白字符,无多余符号的表达式。
ExpressionFactory类为工厂类,其功能为从获取的标准字符串中提取所需要的信息,并生成相应的数据存储类。
类关系图

数据处理方法
本次作业基于第二次作业进行增量式设计,思路基本相同,仅在部分细节方面有所差异,具体如下:
1.将字符串传入Checker类进行检查,采用有限状态机的方法进行操作,对括号进行单独提取,若为表达式因子,则再次调用Checker类中的formatCheck方法进行检查,若为三角函数内嵌套的因子,则调用UnitChecker类中的unitCheck方法进行检查。
2.对传入的字符串进行格式化,并将格式化后的字符串交给工厂类。
3.工厂类按照表达式的生成方法进行字符串分析,将在括号外的两个'+'或'-'之间的字符串传递给项构造方法,并将构造完成的项组成一个ArrayList存储到Term类中。
4.对每一个项构造过程,对传入的字符串进行分析,将在括号外的两个'*'(注意要求'*'前后一个字符不可为'*',即不能构成'**')之间的字符串传递给因子构造方法,并将构造完成的因子组成一个ArrayList存储到Poly类中。
5.对每一个因子构造方法,对传入的字符串进行分析,依次进行表达式、正弦函数、余弦函数、幂函数、常数类型的判断,确定类型后存储至相应的类中,若为表达式因子,则递归调用表达式构造方法,若为三角函数因子,则递归调用因子构造方法。若幂函数或三角函数的指数绝对值大于50,则抛出异常,为“WRONG FORMAT!“。
6.调用最外层的Term类的Derivative方法,递归调用所有数据存储类的Derivative方法,生成并输出最终结果。
度量分析
方法复杂度分析
| method | CogC | ev(G) | iv(G) | v(G) |
|---|---|---|---|---|
| Checker.Checker() | 0.0 | 1.0 | 1.0 | 1.0 |
| Constant.Constant() | 0.0 | 1.0 | 1.0 | 1.0 |
| Constant.Constant(boolean,BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| Constant.derivative() | 0.0 | 1.0 | 1.0 | 1.0 |
| Cos.Cos() | 0.0 | 1.0 | 1.0 | 1.0 |
| Cos.Cos(boolean,BigInteger,Formula) | 0.0 | 1.0 | 1.0 | 1.0 |
| ExpressionFactory.creatChecker() | 0.0 | 1.0 | 1.0 | 1.0 |
| ExpressionFactory.creatProcess() | 0.0 | 1.0 | 1.0 | 1.0 |
| ExpressionFactory.creatScanner() | 0.0 | 1.0 | 1.0 | 1.0 |
| Factory.getFactory() | 0.0 | 1.0 | 1.0 | 1.0 |
| Formula.Formula() | 0.0 | 1.0 | 1.0 | 1.0 |
| Formula.Formula(boolean) | 0.0 | 1.0 | 1.0 | 1.0 |
| Formula.derivative() | 0.0 | 1.0 | 1.0 | 1.0 |
| Formula.isSign() | 0.0 | 1.0 | 1.0 | 1.0 |
| Formula.origin() | 0.0 | 1.0 | 1.0 | 1.0 |
| Poly.Poly() | 0.0 | 1.0 | 1.0 | 1.0 |
| Poly.Poly(boolean,ArrayList) | 0.0 | 1.0 | 1.0 | 1.0 |
| Power.Power() | 0.0 | 1.0 | 1.0 | 1.0 |
| Power.Power(boolean,BigInteger) | 0.0 | 1.0 | 1.0 | 1.0 |
| Sin.Sin() | 0.0 | 1.0 | 1.0 | 1.0 |
| Sin.Sin(boolean,BigInteger,Formula) | 0.0 | 1.0 | 1.0 | 1.0 |
| Term.Term() | 0.0 | 1.0 | 1.0 | 1.0 |
| Term.Term(boolean,ArrayList) | 0.0 | 1.0 | 1.0 | 1.0 |
| UnitCheck.UnitCheck(Checker) | 0.0 | 1.0 | 1.0 | 1.0 |
| Process.formatStr(String) | 1.0 | 1.0 | 2.0 | 2.0 |
| Process.printAns(Formula) | 1.0 | 1.0 | 2.0 | 2.0 |
| Checker.constantSignCheck(char) | 2.0 | 2.0 | 1.0 | 2.0 |
| Checker.cosCCheck(char) | 2.0 | 2.0 | 1.0 | 2.0 |
| Checker.cosOCheck(char) | 2.0 | 2.0 | 1.0 | 2.0 |
| Checker.sinICheck(char) | 2.0 | 2.0 | 1.0 | 2.0 |
| Checker.sinSCheck(char) | 2.0 | 2.0 | 1.0 | 2.0 |
| Constant.origin() | 2.0 | 2.0 | 1.0 | 3.0 |
| UnitCheck.unitConstantSignCheck(char) | 2.0 | 2.0 | 1.0 | 2.0 |
| UnitCheck.unitCosCCheck(char) | 2.0 | 2.0 | 1.0 | 2.0 |
| UnitCheck.unitCosOCheck(char) | 2.0 | 2.0 | 1.0 | 2.0 |
| UnitCheck.unitIndexFirstCheck(char) | 2.0 | 2.0 | 1.0 | 2.0 |
| UnitCheck.unitSinICheck(char) | 2.0 | 2.0 | 1.0 | 2.0 |
| UnitCheck.unitSinSCheck(char) | 2.0 | 2.0 | 1.0 | 2.0 |
| Checker.bracketLeftCheck(char,int) | 3.0 | 2.0 | 1.0 | 3.0 |
| Checker.triBracketLeftCheck(char,int) | 3.0 | 2.0 | 1.0 | 3.0 |
| Cos.origin() | 3.0 | 3.0 | 2.0 | 3.0 |
| MainClass.main(String[]) | 3.0 | 2.0 | 4.0 | 4.0 |
| Power.origin() | 3.0 | 3.0 | 2.0 | 3.0 |
| Sin.origin() | 3.0 | 3.0 | 2.0 | 3.0 |
| UnitCheck.unitBracketEndCheck(char) | 3.0 | 2.0 | 1.0 | 3.0 |
| UnitCheck.unitBracketLeftCheck(char,int) | 3.0 | 2.0 | 1.0 | 3.0 |
| UnitCheck.unitEndCheck(char) | 3.0 | 2.0 | 1.0 | 3.0 |
| UnitCheck.unitTriBracketLeftCheck(char,int) | 3.0 | 2.0 | 1.0 | 3.0 |
| Checker.triSignEndCheck(char) | 4.0 | 3.0 | 1.0 | 4.0 |
| Cos.derivative() | 4.0 | 4.0 | 4.0 | 4.0 |
| Power.derivative() | 4.0 | 4.0 | 4.0 | 4.0 |
| Sin.derivative() | 4.0 | 4.0 | 4.0 | 4.0 |
| Term.derivative() | 4.0 | 2.0 | 2.0 | 5.0 |
| Term.origin() | 4.0 | 2.0 | 2.0 | 5.0 |
| UnitCheck.unitConstantNumCheck(char) | 4.0 | 3.0 | 2.0 | 4.0 |
| UnitCheck.unitPowerXCheck(char) | 4.0 | 3.0 | 1.0 | 4.0 |
| UnitCheck.unitTriBracketEndCheck(char) | 4.0 | 3.0 | 1.0 | 4.0 |
| UnitCheck.unitTriSignEndCheck(char) | 4.0 | 3.0 | 1.0 | 4.0 |
| Checker.indexSecondSignCheck(char) | 6.0 | 4.0 | 3.0 | 6.0 |
| UnitCheck.unitIndexSecondCheck(char) | 6.0 | 4.0 | 2.0 | 6.0 |
| Checker.bracketOverCheck(char) | 7.0 | 5.0 | 3.0 | 7.0 |
| Checker.constantNumCheck(char) | 7.0 | 5.0 | 3.0 | 7.0 |
| Checker.triBracketOverCheck(char) | 7.0 | 5.0 | 3.0 | 7.0 |
| ExpressionFactory.creatPoly(String) | 8.0 | 1.0 | 9.0 | 10.0 |
| Poly.origin() | 8.0 | 3.0 | 2.0 | 5.0 |
| UnitCheck.unitBeginCheck(char) | 10.0 | 8.0 | 3.0 | 10.0 |
| Checker.indexFirstSignCheck(char) | 11.0 | 9.0 | 3.0 | 11.0 |
| Checker.polyMulCheck(char) | 11.0 | 9.0 | 3.0 | 11.0 |
| Checker.polySignCheck(char) | 11.0 | 9.0 | 3.0 | 11.0 |
| Checker.powerXCheck(char) | 11.0 | 9.0 | 3.0 | 11.0 |
| Checker.termBeginCheck(char) | 11.0 | 9.0 | 3.0 | 11.0 |
| Checker.termSignCheck(char) | 11.0 | 9.0 | 3.0 | 11.0 |
| ExpressionFactory.isExpressionUnit(String) | 15.0 | 7.0 | 6.0 | 10.0 |
| ExpressionFactory.creatTerm(boolean,String) | 20.0 | 7.0 | 10.0 | 13.0 |
| Poly.derivative() | 22.0 | 6.0 | 4.0 | 8.0 |
| ExpressionFactory.creatUnit(String) | 31.0 | 12.0 | 10.0 | 13.0 |
| Checker.formatCheck(String) | 42.0 | 11.0 | 8.0 | 37.0 |
| UnitCheck.unitCheck(String) | 42.0 | 11.0 | 8.0 | 35.0 |
| Total | 393.0 | 247.0 | 170.0 | 366.0 |
| Average | 5.04 | 3.17 | 2.18 | 4.69 |
本次作业理论上与第二次作业复杂度应没有太大差异,但实际上平均复杂度大大提高,这主要是因为在字符串格式检查部分采用的有限状态机写法,采用了大量的switch... case...,因而导致字符串检查部分代码臃肿,可读性和维护性降低,但对其他部分影响不大。
类复杂度分析
| class | OCavg | OCmax | WMC |
|---|---|---|---|
| Checker | 6.35 | 36.0 | 127.0 |
| Constant | 1.5 | 3.0 | 6.0 |
| Cos | 2.25 | 4.0 | 9.0 |
| ExpressionFactory | 5.57 | 13.0 | 39.0 |
| Formula | 1.0 | 1.0 | 5.0 |
| MainClass | 2.0 | 2.0 | 2.0 |
| Poly | 3.75 | 8.0 | 15.0 |
| Power | 2.25 | 4.0 | 9.0 |
| Process | 2.0 | 2.0 | 4.0 |
| Sin | 2.25 | 4.0 | 9.0 |
| Term | 3.0 | 5.0 | 12.0 |
| UnitCheck | 4.39 | 34.0 | 79.0 |
| Total | 316.0 | ||
| Average | 4.10 | 9.67 | 26.33 |
通过类复杂度分析可以验证上述结论,其他类复杂度均与第二次作业无明显差异,但两个字符串检查类的复杂度极高,优化角度可以尝试从这一部分入手。
复杂度评价
此次作业的复杂度较高,但主要是由字符串检查部分导致,其他部分基本符合要求,字符串检查与工厂类部分存在较大的优化空间。
测评
本次作业测评无bug需要进行修复,也未能发现其他同学的bug。
优化思路
本次作业优化空间比较大,除了第二次作业提供的优化思路外,还可以从字符串格式检查这一方面入手。
可以尝试采用递归下降分析法简化代码,对字符串进行检查并提取相应的信息,可以有效降低代码的复杂度,提高可读性和可维护性。
重构经历总结
第一次作业尝试进行了面向对象编程,已经考虑了后面可能出现的情况,但是对第二次作业的情况并不能完全适用(具体为可以兼容三角函数但不可兼容表达式因子),需要进行重构。
第二次作业对工作进一步进行拆分,尽可能使这一部分的工作不会影响另一部分的功能,可拓展性比较强,可直接用于第三次作业。
第三次作业在第二次作业的基础上进行工作,没有进行重构,但是在经过老师点拨后发现仍存在着局限性,比如可兼容二阶求导,但运行时间会比较长,后续时间允许的条件下可能会再次进行重构。
心得体会
个人理解
通过这次作业,我对面向对象有了一定的认识,并且能够体会到面向对象的优势,尤其是在面对不断增长的需求时,面向对象就尤为重要。(尤其是在面对各种奇奇怪怪的需求的时候)
个人认为,面向对象的思想,应当完成对各类工作的拆分,类似于几个人的分工合作,一个人仅需要负责自己的那一部分,而不需要考虑其他部分是怎么完成的,最终由主函数进行串联。换句话说,也就是整体思路上面向对象,具体某个细节上面向过程。
存在问题
在本次作业中,个人感觉自己仍存在着一些问题:
1.递归下降分析法仍未掌握,最直接的结果就是导致字符串格式检查部分代码冗长,可读性、可维护性低。
2.封装的思想还没有完全掌握,对类里面存储的数据的保护仍不太熟练。
3.字符串处理不熟练,尤其是对于类似于括号嵌套的部分并没有比较好的思路。
4.未能实现比较好的优化,本单元的作业仅仅实现了比较基础的优化,比如去括号、删除含0因子的项等,但是对于类似于合并同类项、三角函数化简等操作在第二次、第三次作业均未能实现,主要原因还是由于字符串处理部分存在的问题。
个人期望
总体来说,本次作业自己的收获还是很高的。希望自己能够汲取在这次作业里面犯错的经验,避免自己和其他同学踩过的坑,在后续的任务中不断提高自己吧。


浙公网安备 33010602011771号