BUAA_OO_2020_Unit1_Summary
简述
Unit1是OO课程的序幕,旨在帮助学生提升鲁棒性和层次化设计的能力,初步形成面向对象的思想,并掌握一些常用的测试方法。本文从程序结构、自身bug分析、hack策略、风格重构及心得体会五方面展开,总结在Unit1中的收获。
程序结构
第一次作业
UML图
Statistic统计结果
Metrics度量分析
方法复杂度
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Expression.Expression(String) | 1 | 1 | 1 |
Expression.getDiffExpression() | 1 | 1 | 1 |
Expression.setDiffExpression() | 3 | 12 | 12 |
Expression.setDiffInfo() | 3 | 3 | 3 |
Expression.setInfo() | 1 | 4 | 4 |
Expression.setItemOpMap() | 1 | 3 | 3 |
FormalExp.getExpressionUnitPattern() | 1 | 1 | 1 |
FormalExp.getItemPattern() | 1 | 1 | 1 |
FormalExp.getPowFunPattern() | 1 | 1 | 1 |
Item.Item(String) | 1 | 1 | 1 |
Item.getCoe() | 1 | 1 | 1 |
Item.getPowFun() | 1 | 1 | 1 |
Item.setInfo() | 1 | 6 | 6 |
MainClass.main(String[]) | 1 | 1 | 1 |
PowFun.PowFun(String) | 1 | 1 | 1 |
PowFun.equals(Object) | 3 | 1 | 3 |
PowFun.getExp() | 1 | 1 | 1 |
PowFun.hashCode() | 1 | 1 | 1 |
PowFun.setExp() | 1 | 3 | 4 |
Total | 25 | 44 | 47 |
Average | 1.32 | 2.32 | 2.47 |
类复杂度
Class | OCavg | WMC |
---|---|---|
Expression | 4 | 24 |
FormalExp | 1 | 3 |
Item | 2.25 | 9 |
MainClass | 1 | 1 |
PowFun | 2 | 10 |
Total | 47 | |
Average | 2.47 | 9.4 |
依赖
Class | Cyclic | Dcy | Dcy* | Dpt | Dpt* |
---|---|---|---|---|---|
Expression | 0 | 3 | 3 | 1 | 1 |
FormalExp | 0 | 0 | 0 | 3 | 4 |
Item | 0 | 2 | 2 | 1 | 2 |
MainClass | 0 | 1 | 4 | 0 | 0 |
PowFun | 0 | 1 | 1 | 2 | 3 |
简评
-
第一次作业较为简单,仅实现幂函数求导的功能,且在化简方面不设置障碍。为完成从面相过程到面向对象的过渡,贴近课程的要求,我对类做了细致的划分,共有MainClass、PowFun、Item、Expression、FormalExp五类。
- 幂函数类PowFun以指数为主要属性;项类Item以系数和幂函数为主要属性;表达式类Expression以Item的集合(以HashMap
的形式表示,便于化简)为主要属性,在该类内部得到求导后的表达式对应的Item的集合。整个划分从数学表达式的组成特点着手。 - 形式化表述类FormalExp是一个将正则表达式以静态属性的形式存放,并以静态方法返回的类,它起到统一管理正则表达式的作用,当想要用某正则表达式之时只要调用形式化表述类中相应的静态方法即可,而不必将各种正则表达式定义在各类中,引起混乱,不便于修改。
- 幂函数类PowFun以指数为主要属性;项类Item以系数和幂函数为主要属性;表达式类Expression以Item的集合(以HashMap
-
通过UML图,可以看出第一次作业的主要类间关系为组合关系,与设计初衷相吻合。UML图中显示PowFun对象亦是Expression对象的组成成分,看似有些不符合数学表达式的特点,实际上这是由于Expression类中的Item集合表示成了幂函数-系数键值对构成的HashMap所致。
-
通过Metrics度量,可以看出第一次作业的复杂度主要集中在Expression类的setDiffExpression方法。该方法用于设置Expression类中的diffExpression属性,即求导后所得表达式的字符串表示。复杂度高的原因是因为本人在利用diffInfo(求导后所得表达式的项的集合,以HashMap
的形式表示)得到表达式时,为保证表达式长度尽可能短(高性能),引入了许多if-else分支语句进行特判。其中,保证输出表达式字符串首项非负的环节,本人是通过将正、负项分离(先保留每一项前面的+ / -符号),最后去掉正项开头的+号并与负项拼接的方法实现的。这一过程可以通过将HashMap转为包含其entry的TreeSet,让TreeSet内部的entry元素根据Value(BigInteger)的值由高到低排序来实现。 PS: 实测发现排序问题中,利用TreeSet时的程序运行效率远好于使用Collections类中的静态方法sort。
第二次作业
UML图
Statistic统计结果
Metrics度量分析
方法复杂度
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
ConFactor.ConFactor(BigInteger) | 1 | 1 | 1 |
ConFactor.ConFactor(String) | 1 | 1 | 1 |
ConFactor.getValue() | 1 | 1 | 1 |
ConFactor.setValue(String) | 1 | 2 | 2 |
CosFun.CosFun(BigInteger) | 1 | 1 | 1 |
CosFun.CosFun(String) | 1 | 1 | 1 |
CosFun.getDiffFactors() | 3 | 1 | 3 |
CosFun.toString() | 2 | 2 | 2 |
Expression.Expression(String) | 1 | 1 | 1 |
Expression.getDiffInfo() | 1 | 3 | 3 |
Expression.getExpressionStr(HashMap |
4 | 12 | 12 |
Expression.printDiffExpression() | 2 | 5 | 5 |
Expression.setInfo() | 8 | 8 | 13 |
FactorFactory.makeFactor(String) | 5 | 4 | 5 |
FormalExp.getConFactorPattern() | 1 | 1 | 1 |
FormalExp.getCosFunPattern() | 1 | 1 | 1 |
FormalExp.getExpressionUnitPattern() | 1 | 1 | 1 |
FormalExp.getPowFunPattern() | 1 | 1 | 1 |
FormalExp.getSinFunPattern() | 1 | 1 | 1 |
InfoDealer.dealInfo(Item,HashMap |
1 | 2 | 2 |
Item.Item() | 1 | 1 | 1 |
Item.Item(BigInteger,VarFactorInfo) | 1 | 1 | 1 |
Item.getCoe() | 1 | 1 | 1 |
Item.getDiffItems() | 4 | 6 | 7 |
Item.getVarFactorInfo() | 1 | 1 | 1 |
Item.multFactor(Factor) | 1 | 3 | 3 |
MainClass.main(String[]) | 1 | 2 | 2 |
PowFun.PowFun(BigInteger) | 1 | 1 | 1 |
PowFun.PowFun(String) | 2 | 1 | 2 |
PowFun.getDiffFactors() | 2 | 2 | 3 |
PowFun.toString() | 3 | 3 | 3 |
SimplifyDealer.getSimInfo(HashMap |
5 | 6 | 11 |
SimplifyDealer.getSimInfos(HashMap |
3 | 2 | 3 |
SimplifyDealer.simplifyOneSub(BigInteger,VarFactorInfo,HashMap |
2 | 9 | 10 |
SimplifyDealer.simplifyS2AddC2(BigInteger,VarFactorInfo,HashMap |
3 | 3 | 3 |
SimplifyDealer.simplifyS4SubC4(BigInteger,VarFactorInfo,HashMap |
2 | 5 | 6 |
SimplifyRet.SimplifyRet(VarFactorInfo,ArrayList ) | 1 | 1 | 1 |
SimplifyRet.getItems() | 1 | 1 | 1 |
SimplifyRet.getVarFactorInfoT() | 1 | 1 | 1 |
SinFun.SinFun(BigInteger) | 1 | 1 | 1 |
SinFun.SinFun(String) | 1 | 1 | 1 |
SinFun.getDiffFactors() | 3 | 1 | 3 |
SinFun.toString() | 2 | 2 | 2 |
VarFactor.VarFactor(BigInteger) | 1 | 1 | 1 |
VarFactor.VarFactor(String,String) | 1 | 1 | 1 |
VarFactor.getExp() | 1 | 1 | 1 |
VarFactor.multiply(VarFactor) | 1 | 1 | 1 |
VarFactor.setExp(String,String) | 1 | 2 | 3 |
VarFactorInfo.VarFactorInfo() | 1 | 1 | 1 |
VarFactorInfo.VarFactorInfo(BigInteger,BigInteger,BigInteger) | 1 | 1 | 1 |
VarFactorInfo.equals(Object) | 2 | 1 | 4 |
VarFactorInfo.fuseInfoOf(VarFactor) | 2 | 2 | 5 |
VarFactorInfo.getCosFunExp() | 1 | 1 | 1 |
VarFactorInfo.getPowFunExp() | 1 | 1 | 1 |
VarFactorInfo.getSinFunExp() | 1 | 1 | 1 |
VarFactorInfo.getVarFactorList() | 1 | 1 | 1 |
VarFactorInfo.hashCode() | 1 | 1 | 1 |
VarFactorInfo.toString() | 3 | 4 | 4 |
Total | 100 | 125 | 153 |
Average | 1.72 | 2.16 | 2.64 |
类复杂度
Class | OCavg | WMC |
---|---|---|
ConFactor | 1.25 | 5 |
CosFun | 1.75 | 7 |
Expression | 6.8 | 34 |
FactorFactory | 5 | 5 |
FormalExp | 1 | 5 |
InfoDealer | 2 | 2 |
Item | 2.33 | 14 |
MainClass | 1 | 1 |
PowFun | 2 | 8 |
SimplifyDealer | 6.2 | 31 |
SimplifyRet | 1 | 3 |
SinFun | 1.75 | 7 |
VarFactor | 1.4 | 7 |
VarFactorInfo | 1.8 | 18 |
Total | 147 | |
Average | 2.53 | 10.5 |
依赖
Class | Cyclic | Dcy | Dcy* | Dpt | Dpt* |
---|---|---|---|---|---|
ConFactor | 0 | 2 | 2 | 6 | 11 |
CosFun | 1 | 5 | 5 | 3 | 9 |
Expression | 0 | 8 | 13 | 1 | 1 |
FactorFactory | 0 | 6 | 7 | 1 | 2 |
FormalExp | 0 | 0 | 0 | 6 | 12 |
InfoDealer | 0 | 2 | 9 | 2 | 3 |
Item | 0 | 4 | 8 | 4 | 5 |
MainClass | 0 | 1 | 14 | 0 | 0 |
PowFun | 0 | 4 | 4 | 2 | 8 |
SimplifyDealer | 0 | 4 | 11 | 1 | 2 |
SimplifyRet | 0 | 2 | 9 | 1 | 3 |
SinFun | 1 | 5 | 5 | 3 | 9 |
VarFactor | 0 | 1 | 1 | 5 | 11 |
VarFactorInfo | 0 | 4 | 7 | 5 | 6 |
Interface | Cyclic | Dcy | Dcy* | Dpt | Dpt* |
---|---|---|---|---|---|
Factor | 0 | 0 | 0 | 8 | 13 |
简评
-
第二次作业在第一次作业的基础上,增加了正、余弦函数因子。由这个转变以及数学表达式的特点来看,因子是本单元作业的重要迭代点。因此,在第二次作业中,我就对因子类进行了细分,并建立统一的管理接口Factor。
-
正则表达式是处理输入的一个强有力的工具,但正则表达式作为一种非有限状态自动机,若析取单元过长,极易导致爆栈。因此,在设计第二次作业之前,我转变了第一次作业中采用的整个表达式匹配捕获关键信息的方法,将因子及其前面的“+”||"-"||"*"作为析取单元,缩小正则表达式的匹配颗粒度,保证程序性能在需求变多时仍能稳固,且相当符合数学表达式的结构特征。
-
在表达式析取环节,若引入大量的if-else语句来判断析取出来的因子类型,明显有点让Expression类做了一些它本不该做的事的味道。因此,我利用工厂模式,在表达式析取环节根据析取单元所对应的字符串,让工厂制造相应的因子“产品”。第二次作业中由于所有因子只有主体部分,而没有嵌套部分,工厂中设一个单参数制造方法即可,例如向工厂送入字符串cos(x)**-2,工厂即返回一个指数为-2的余弦因子。
-
输入合法性判断方面,通过在表达式析取环节抛出异常的方法来捕捉非法表达式,由于全程幂函数因子仅在表达式析取时进行建立,因此对于幂函数幂指数异常的处理,我仅在幂函数的构造方法中加以判断。
-
优化方面,基于贪心算法,借鉴深搜中记录节点访问情况的思想,通过“结伴”取出的方式,对a*sin**2+b*cos**2、1-sin**2||1-cos**2、sin**4-cos**4这三种情况进行优化。
-
通过UML图,可以看出第二次作业的主要类间关系仍为组合关系。同时,各种因子单独设类,满足一定的继承关系,并实现Factor接口。
-
通过Metrics度量,可以看出第二次作业的复杂度一是集中在简化器SimplifyDealer,二还是集中在Expression类。简化器中,已对三种简化情况进行三类方法的划分,但由于算法较为暴力,故所呈现的复杂度较高。Expression类中的复杂度体现在getExpressionStr方法(将存储表达式项信息的HashMap转为字符串)及setInfo方法(析取表达式,得到项信息)。getExpressionStr方法的复杂原因和改进方法类似于第一次作业简评的中的setDiffExpression方法。setInfo方法的复杂原因主要是表达式析取环节利用了捕获组的知识,要对m.group(String)进行判断,在边界条件较多的情形下,不得不使用一些更细致的捕获组标签,这也大大增加了if-else分支的数量。
-
第二次作业的整体依赖关系较为正常,DPI=1。
-
关于第二次作业的更多细节见如下思维导图:
第三次作业
UML图
Statistic统计结果
Metrics度量分析
方法复杂度
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
ConFactor.ConFactor(BigInteger) | 1 | 1 | 1 |
ConFactor.ConFactor(String) | 1 | 1 | 1 |
ConFactor.equals(Object) | 2 | 1 | 2 |
ConFactor.getDiffFactors() | 1 | 1 | 1 |
ConFactor.getValue() | 1 | 1 | 1 |
ConFactor.hashCode() | 1 | 1 | 1 |
ConFactor.setValue(String) | 1 | 2 | 2 |
ConFactor.toString() | 1 | 1 | 1 |
CosFun.CosFun(BigInteger,Factor) | 1 | 1 | 1 |
CosFun.CosFun(String,String) | 1 | 1 | 1 |
CosFun.getDiffFactors() | 3 | 2 | 4 |
CosFun.toString() | 2 | 3 | 3 |
ExpressDealer.ExpressDealer(String) | 1 | 1 | 1 |
ExpressDealer.getInterFactors() | 1 | 1 | 1 |
ExpressDealer.getRetExpress() | 1 | 1 | 1 |
ExpressDealer.setRetInfo() | 5 | 5 | 8 |
Expression.Expression(String) | 1 | 1 | 1 |
Expression.equals(Object) | 5 | 2 | 5 |
Expression.expCheck(String) | 4 | 3 | 4 |
Expression.getDiffExpressionStr() | 2 | 1 | 2 |
Expression.getDiffInfo() | 1 | 3 | 3 |
Expression.getExpressionStr() | 1 | 1 | 1 |
Expression.getExpressionStr(HashMap |
4 | 12 | 12 |
Expression.getInfo() | 1 | 1 | 1 |
Expression.hashCode() | 1 | 2 | 2 |
Expression.printDiffExpression() | 1 | 1 | 1 |
Expression.setInfo() | 8 | 9 | 14 |
ExpressionFactor.ExpressionFactor(String) | 1 | 1 | 1 |
ExpressionFactor.equals(Object) | 2 | 1 | 2 |
ExpressionFactor.getDiffFactors() | 1 | 1 | 1 |
ExpressionFactor.getExp() | 1 | 1 | 1 |
ExpressionFactor.getExpression() | 1 | 1 | 1 |
ExpressionFactor.hashCode() | 1 | 1 | 1 |
ExpressionFactor.toString() | 1 | 1 | 1 |
FactorFactory.makeFactor(String) | 3 | 3 | 3 |
FactorFactory.makeFactor(String,String) | 12 | 11 | 14 |
FormalExp.getConFactorPattern() | 1 | 1 | 1 |
FormalExp.getCosFunPattern() | 1 | 1 | 1 |
FormalExp.getExpPattern() | 1 | 1 | 1 |
FormalExp.getExpressionFactorPattern() | 1 | 1 | 1 |
FormalExp.getExpressionUnitPattern() | 1 | 1 | 1 |
FormalExp.getPowFunPattern() | 1 | 1 | 1 |
FormalExp.getSinFunPattern() | 1 | 1 | 1 |
FormalExp.getTriFunPattern() | 1 | 1 | 1 |
FormalExp.getZeroPattern() | 1 | 1 | 1 |
InfoDealer.dealInfo(Item,HashMap |
2 | 4 | 4 |
Item.Item() | 1 | 1 | 1 |
Item.Item(BigInteger,NonConFactorInfo) | 1 | 1 | 1 |
Item.getCoe() | 1 | 1 | 1 |
Item.getDiffItems() | 4 | 5 | 6 |
Item.getNonConFactorInfo() | 1 | 1 | 1 |
Item.multFactor(Factor) | 1 | 6 | 6 |
Item.multFactors(ArrayList ) | 1 | 2 | 2 |
MainClass.main(String[]) | 2 | 2 | 3 |
NonConFactorInfo.NonConFactorInfo() | 1 | 1 | 1 |
NonConFactorInfo.NonConFactorInfo(PowFun,HashSet ,HashSet ,ArrayList ) | 1 | 1 | 1 |
NonConFactorInfo.clone() | 1 | 1 | 1 |
NonConFactorInfo.equals(Object) | 11 | 8 | 14 |
NonConFactorInfo.fuseInfoOf(NonConFactor) | 7 | 10 | 15 |
NonConFactorInfo.getCosFuns() | 1 | 1 | 1 |
NonConFactorInfo.getExpressionFactors() | 1 | 1 | 1 |
NonConFactorInfo.getNonConFactorList() | 1 | 7 | 7 |
NonConFactorInfo.getPowFun() | 1 | 1 | 1 |
NonConFactorInfo.getSinFuns() | 1 | 1 | 1 |
NonConFactorInfo.hashCode() | 1 | 1 | 1 |
NonConFactorInfo.onlyContainOneExpressionFactor() | 1 | 4 | 4 |
NonConFactorInfo.toString() | 3 | 4 | 4 |
PowFun.PowFun(BigInteger) | 1 | 1 | 1 |
PowFun.PowFun(String) | 1 | 1 | 1 |
PowFun.equals(Object) | 2 | 1 | 2 |
PowFun.getDiffFactors() | 2 | 2 | 3 |
PowFun.hashCode() | 1 | 1 | 1 |
PowFun.toString() | 3 | 3 | 3 |
PowFun.toString1() | 2 | 2 | 2 |
SinFun.SinFun(BigInteger,Factor) | 1 | 1 | 1 |
SinFun.SinFun(String,String) | 1 | 1 | 1 |
SinFun.getDiffFactors() | 3 | 2 | 4 |
SinFun.toString() | 2 | 3 | 3 |
TriFun.TriFun(BigInteger,Factor) | 1 | 1 | 1 |
TriFun.TriFun(String,String,String) | 1 | 1 | 1 |
TriFun.equals(Object) | 2 | 2 | 3 |
TriFun.getFactor() | 1 | 1 | 1 |
TriFun.hashCode() | 1 | 1 | 1 |
VarFactor.VarFactor(BigInteger) | 1 | 1 | 1 |
VarFactor.VarFactor(String,String) | 1 | 1 | 1 |
VarFactor.getExp() | 1 | 1 | 1 |
VarFactor.multiply(VarFactor) | 6 | 3 | 6 |
VarFactor.setExp(String,String) | 1 | 2 | 3 |
Total | 165 | 186 | 228 |
Average | 1.88 | 2.11 | 2.59 |
类复杂度
Class | OCavg | WMC |
---|---|---|
ConFactor | 1.25 | 10 |
CosFun | 2.25 | 9 |
ExpressDealer | 2.75 | 11 |
Expression | 4.18 | 46 |
ExpressionFactor | 1.14 | 8 |
FactorFactory | 7.5 | 15 |
FormalExp | 1 | 9 |
InfoDealer | 4 | 4 |
Item | 2.57 | 18 |
MainClass | 2 | 2 |
NonConFactor | 0 | |
NonConFactorInfo | 3.54 | 46 |
PowFun | 1.71 | 12 |
SinFun | 2.25 | 9 |
TriFun | 1.2 | 6 |
VarFactor | 2.4 | 12 |
Total | 217 | |
Average | 2.47 | 13.56 |
依赖
Class | Cyclic | Dcy | Dcy* | Dpt | Dpt* |
---|---|---|---|---|---|
ConFactor | 0 | 2 | 2 | 6 | 12 |
CosFun | 10 | 7 | 15 | 4 | 11 |
ExpressDealer | 0 | 0 | 0 | 2 | 12 |
Expression | 10 | 8 | 15 | 5 | 11 |
ExpressionFactor | 10 | 4 | 15 | 4 | 11 |
FactorFactory | 10 | 10 | 15 | 3 | 11 |
FormalExp | 0 | 0 | 0 | 6 | 13 |
InfoDealer | 10 | 4 | 15 | 1 | 11 |
Item | 10 | 6 | 15 | 2 | 11 |
MainClass | 0 | 1 | 16 | 0 | 0 |
NonConFactor | 0 | 1 | 1 | 4 | 12 |
NonConFactorInfo | 10 | 6 | 15 | 3 | 11 |
PowFun | 10 | 4 | 15 | 5 | 11 |
SinFun | 10 | 7 | 15 | 4 | 11 |
TriFun | 10 | 3 | 15 | 4 | 11 |
VarFactor | 10 | 6 | 15 | 5 | 11 |
Interface | Cyclic | Dcy | Dcy* | Dpt | Dpt* |
---|---|---|---|---|---|
Factor | 0 | 0 | 0 | 11 | 14 |
简评
-
第三次作业在第二次作业的基础上,增加了嵌套因子、表达式因子。由于在第二次作业中做了较多的可迭代方面的考虑,第三次作业的设计基本上可沿用第二次作业。
-
从第二次作业过渡到第三次作业所面临的最大问题时:第二次作业的析取单元可以直接由正则表达式表征,而第三次作业中的析取单元可能含有嵌套因子,无法利用正则表达式实现这样一个递归结构。在这种情况下,我利用替换法。在析取并建立一个表达式的过程中,将表达式表征为一个主干表达式及若干个嵌套成分构成的队列。若碰上带#的析取单元,则从嵌套成分队列中弹出一个嵌套成分,作为建立因子过程中的附加信息。(eg: sin(cos(x))+(2*x) --> sin(#)+(#)及{cos(x), 2*x})
-
工厂模式在第三次作业中仍发挥巨大作用。工厂的制造方法方面,根据我在表达式析取环节的设计,第三次作业中采用单、双参数两类制造方法,不含嵌套成分的因子字符串直接传入单参数制造方法即可;而含有嵌套成分的因子字符串传入单参数制造方法时,可将替换后得到的主干成分和嵌套成分传给双参数制造方法进行解析,也可以直接在工厂外部将主干成分和嵌套成分传给双参数制造方法。
-
输入合法性判断方面,在第三次作业中,由于三类在输入表达式中有幂指数限制的变量因子需在程序中多次建立,故直接在它们的构造方法中进行异常处理是不科学的,因此我在表达式输入的一开始就通过正则表达式匹配捕获幂指数并进行检查,对于其他方面的异常,同第二次作业一样在解析的过程中不断抛出异常即可。
-
优化方面,第三次作业中基于替代的方法,即用更简单的因子代替复杂的因子,实现了括号的优化。同时,对于仅有一个表达式因子的项,用因子内部表达式的若干项代替。
-
通过UML图,可以看到第三次作业的UML图虽然变得复杂不少,但归根到底还是以继承和组合为主旋律。同时,工厂成为了一个强有力的创造者。
-
通过Metric度量,可以看出第三次作业的复杂度除集中在第二次作业简评中已经分析的Expression类及其相关方法外,还体现在工厂制造方法、NonConFactorInfo类的equals方法和fuseInfoOf方法上。工厂制造方法中由于要不断判断信息的类型,故if-else分支数较多。而NonFactorInfo类中的方法复杂度的降低,则可以通过NonConFactorInfo属性的合并来实现,具体方案见重构部分。
-
第三次作业的DPI=1。从整体依赖关系上看,出现了大量的Cyclic,经分析,这是由于我的代码风格不良所致的,完全可以缓和。工厂类中由于要不断地递归调用自身求得更简单的“产品”,以提升作业的性能,出现较多的Cyclic是正常的。但我的整个设计没有完全将处理字符串信息的工作交付给工厂,还为三角函数因子提供处理字符串信息的构造方法,并在三角函数因子抽象类中调用工厂,导致工厂高Cyclic向因子类的传递。我会在重构环节给出较好的改进方案。
-
关于第三次作业的更多细节见如下思维导图:
自身bug分析
第一次作业
-
中测、强测、互测中均未出现bug
-
中测提交后,私下测试过程中未发现bug
第二次作业
-
中测、强测、互测中均未出现bug
-
中测提交后,私下测试过程中未发现bug
第三次作业
-
中测、强测、互测中均未出现bug
-
中测提交后,私下测试过程中发现以下bug:
-
运算过程中出现变量因子的指数大于50的情况,有时会被判定为WF
- 所在类:VarFactor;所在方法:构造方法VarFactor(String, String)
- 出现原因:迭代开发过程中沿用第二次设计,即在变量因子的构造方法中判断指数;但在第三次作业中,变量因子构造方法的调用时机不局限在表达式析取的过程中,而会在递归化简的过程中不断进行,这会使由递归化简产生的指数大于50的变量因子触发异常。
- 解决方案:直接在MainClass类中调用Expression类的静态方法expCheck,借助正则表达式来判断stdin中出现的所有指数是否合法。
-
sin(0)**0所对应值为0
- 所在类:FactorFactory;所在方法:makeFactor(String, String)
- 出现原因:利用工厂作简化时,边界情况考虑不周。
- 解决方案:在工厂类中对sin(0)**0的情况进行特判。
-
向Item缓存中投入因子时,已投入成分被修改
- 所在类:VarFactor;所在方法:multiply(VarFactor)
- 出现原因:迭代开发过程中沿用第二次作业的设计,让multiply为修改VarFactor对象指数而无返回值的方法;但在第三次作业中,由于程序中存储结构的变化,发生深浅拷贝所致的错误。
- 解决方案:让multiply返回新实例化的VarFactor对象。
-
总结
-
三次作业均采用手动测试+自动测试的方式,通过手动测试检测程序的边界抗压能力,通过自动测试检测程序的基本功能。
-
对于手动测试,在前两次作业中,重点构造测试正则表达式样例,以保证正则表达式解析的正确性;在第三次作业中,重点构造包含复杂嵌套、易引发程序深层递归、易导致TLE的样例,并结合题面中设定的各边界点进行测试。
-
对于自动测试,利用Python程序随机生成表达式,基于linux命令行设计批处理脚本,向error.txt文件输出产生错误的表达式;与自动测试有关的工作文件、记录文件打包放在i编译后自动更新的out/production/U1HX目录下(同.class文件并行放置)。
-
第三次作业中的私下测试发现bug,前二者来源于手动测试,第三者来源于自动测试,由此可见手动、自动测试相互结合的重要性。
hack策略
第一次作业
-
本次作业互测中共发现2个他人bug
-
stdin: 5*x**0
- stdout: 空串
- hack策略:手动构造边界样例
- hack启发点:该同学的程序中,未对求导后的表达式不含任何项的边界情况进行特判。
-
stdin: 0+0*x
- stderror: java.lang.StringIndexOutOfBoundsException
- hack策略:手动构造边界样例
- hack启发点:该同学的程序中,大量使用基本数组及charAt方法,容易引发数组越界惨案。
-
第二次作业
-
本次作业互测中共发现3个他人bug
-
stdin: x**10000*x**10000
- stdout: WRONG FORMAT!
- hack策略:手动构造边界样例
- hack启发点:该同学的程序中,只要生成系数大于10000的幂函数,即引发异常触发WF判断。
-
stdin: sin(x)**7+3*sin(x)**5*cos(x)**2+3*sin(x)**3*cos(x)**4+sin(x)*cos(x)**6
- stdout: cos(x)**3+sin(x)**2*cos(x)0*sin(x)**4*cos(x)
- hack策略:自动测试样例
-
stdin: x**559*-712*sin(x)*-50*cos(x)**-1*-91
- stdout: -3239600*x**559*cos(x)**-2-1810936400*x**558*sin(x)*cos(x)**-10*x**559*sin(x)**2*cos(x)**-2
- hack策略:自动测试样例
-
第三次作业
-
本次作业互测中共发现1个他人bug
-
stdin: + -9++ ( +-71*-6+ + 4*47*x)* sin (cos(cos(x ) ) )
- stdout: (4*47)*(sin((cos((cos(x))))))-(71)*-6+4*47*x*(cos((cos((cos(x))))))*(-1*sin((cos(x))))*-1*sin(x)
- hack策略:自动测试样例
-
总结
-
三次hack的过程中,均采用与自身程序测试相同的方法,即手动测边界,自动测功能。
-
第一次作业中成功hack的点均来源于手动样例,主要是因为第一次作业的问题较简单,基本上通过中测的同学都可实现基本功能的满足,但仍可能忽略一些边界条件;另外,第一次作业的边界条件可以以一个较大的覆盖面罗列出来,若程序中存在边界方面的bug,手动测试将有一个较大的概率发现问题。
-
后三次作业中,除第二次作业中的第一个bug来源于手动样例,其他都来源于自动样例,主要是因为第二次作业和第三次作业的问题较复杂,边界条件的全面罗列难以实现,而此时基本功能出错的概率大于第一次作业,故自动测试成为发现bug的有力手段。
风格重构
-
关于属性设置
-
第二次作业中,变量因子信息类VarFactorInfo的属性设置如下:
private PowFun powFun; private SinFun sinFun; private CosFun cosFun;
-
第三次作业中,从VarFactorInfo拓展上升所得的非常量因子信息类NonConFactorInfo的属性设置如下:
private PowFun powFun; private HashSet
sinFuns; private HashSet cosFuns; private ArrayList expressionFactors; -
第三次作业中,模仿第二次作业进行属性设置,考虑到正、余弦函数因子若内部嵌套因子不同则无法乘法合并的事实,以正HashSet的形式分别存储正、余弦函数因子。进一步从容器及设计层次的角度去想,既然我为幂函数、正弦函数、余弦函数因子都建立了统一的抽象类变量因子类(VarFactor),那么完全可以将NonConFactorInfo的属性设置更改为:
private HashSet
varFactors; private ArrayList expressionFactors; 即用一个存放VarFactor的HashSet涵盖原设计中三个关于各类变量因子的属性。这样做,可以有效减少NonConFactorInfo类中fuseInfoOf方法中的分支数量,并让getNonConFactorList、equals等方法的书写更加简洁。
-
-
关于工厂模式的应用
-
通过实验课的学习,我们了解到工厂模式是一种能够起到解耦作用和提高可拓展性的创建类模式,主要分为简单工厂模式、工厂方法模式和抽象工厂模式。对于工厂模式我有以下理解:
- 当我们仅想将外界“信息”传给工厂,让工厂生产基本“产品”时,可以采用简单工厂模式,仅仅定义一个主工厂类。
- 工厂方法模式则是通过增加工厂类的数量,划分出生产各种基本“产品”的小工厂,让主工厂作为小工厂的统一管理接口,达到进一步提升工厂优势的目的。但对于教程中那种单纯的工厂方法模式,主工厂仅仅是小工厂的接口而没有什么内在作用,而这要求我们向工厂索要“产品”前必须弄清“产品”的类型,以实例化相对应的小工厂。
- 抽象工厂模式则常用于各类“产品”满足一定的等级结构的时候。
-
我在后两次作业中均使用简单工厂模式,具体介绍见程序结构部分。
-
风格方面的不足之处:
- 简单工厂模式所有工作均在主工厂里进行,导致第三次作业我的主工厂双参数制造方法各if-else分支中包含大量的递归化简部分,过于繁琐。
- 由于在设计前对工厂模式的精髓感受不深,采用传统的在变量因子抽象类中通过正则表达式析取次数的做法,而这项工作完全可以交付给工厂完成。
-
一种优化思路:
- 采用一种由简单工厂向工厂方法过渡的模式,为各类因子开设子工厂。
- 工厂外部每次给工厂传递“信息”,均先传给主工厂,主工厂只判断“信息”的类别而不深入剖析“信息”的细节,根据“信息”类别调用相应的子工厂,将“信息”传给子工厂。
- 子工厂再剖析“信息”的细节,得到“产品”的重要参数,并分析相较于自己生产的“产品”,是否可以将“信息”做一些同质的处理,让其他子工厂好伙伴利用处理后的“信息”生产出更为简便的产品,作为最终返回给主工厂,主工厂再返回给工厂外部的“产品”呢?
- 在各个因子类中,只需开设从具体属性值直接切入的构造方法,而不必开设从字符串中抽取具体属性值的构造方法。让因子专注于自身属性的管理,而将通过解析字符串返回因子的工作交付给工厂,使因子和工厂所要承担的”责任“更加明确,使程序更加object-oriented。
-
可应用于第三次作业中的优化后的工厂模式概括为如下思维导图:
-
心得体会
收获
-
初步了解面向对象程序的构成,培养面向对象的思维。
-
对接口、抽象类、继承、实现、多态、封装、深浅拷贝等语法知识有更具象的理解。
-
学会简单地使用工厂模式来创建对象。
-
掌握构建简易评测机的思路,了解java的Linux命令行编译运行过程,提升了测试程序和发现bug的能力。
-
对项目的迭代开发过程有更深刻的思考。在此总结迭代开发的几大要点如下:
- 抓住迭代上升点,缩小解析位元,如在作业中对各因子单独设类。
- 突出层次框架,实现问题转化,如在第三次作业中使用迭代法。
- 分析前后版本的异同点,注意迭代前后类或方法调用时机的变化;方法设计的安全性尽可能不局限于当前版本,要考虑到后续版本可能引发的后果。正如我在第三次作业私下测试中发现的指数判断bug和深浅拷贝bug,均是由于第二次作业中采用不够安全的做法所致。
- 积极使用工厂模式进行解耦,加大根据”信息“创建对象的灵活度。
- 始终保持良好的程序设计习惯,例如循环变量提前存储、关注深浅拷贝问题等,不至于因版本的升级而让程序运行效率严重下滑甚至是出现bug。
不足
-
通过反思与总结,自己在书写代码过程中还存在对类的“责任”意识认识不足,统一管理思想不深等风格问题。
-
后两次作业互测均有未发现的边界bug,说明手动构造边界条件的能力及研读代码细节的能力还有所欠缺。