面向对象第一单元个人总结
这次主要依据作业要求中的四个层次来分析这一个月的学习。从对oo,java一无所知到逐步探索,在作业压力下学习各种知识。现在就是总结的时候了。
(1)基于度量来分析自己的程序结构
第一次作业:
- 完全使用字符状态机。完全没有结构,几个方法都是截断出来的。像newNumber_t这种完全忘了是做什么用的代码。
小总结:不熟悉java,完全没有oo思想,不会用git,对代码风格没有概念。
强行没有使用正则表达式。对时间估计错误。
收获:git使用,java语法。Arraylist使用。复习了状态机(…)。
然后在讨论课上对于正则的使用有了了解,清楚基本的面向对象思路。尝试对于数据进行封装。
见下方表格,ev(G),iv(G) ,v(G)都十分之高,可见代码复杂度极高,可维护性差。
摘自百度百科--圈复杂度大说明程序代码可能质量低且难于测试和维护,根据经验,程序的可能错误和高的圈复杂度有着很大关系。
Method | ev(G) | iv(G) | v(G) |
Weekk.addSign(BigInteger,char[],int) | 6 | 1 | 8 |
Weekk.addTD(BigInteger,BigInteger) | 1 | 2 | 2 |
Weekk.clearSign(int,char[]) | 1 | 1 | 4 |
Weekk.main(String[]) | 1 | 14 | 14 |
Weekk.newNumber_t(int,String,BigInteger) | 1 | 3 | 4 |
Weekk.readLine() | 1 | 1 | 1 |
Weekk.rightFormat(String) | 30 | 29 | 48 |
Class | OCavg | WMC | |
hhh.Weekk | 8.29 | 58 | |
Project | v(G)avg | v(G)tot | |
project | 11.57 | 81 |
第二次作业:
小总结:基本完善较好,有了基本的面向对象的改进,代码可读性大大增强。风格和强测都全过了,互测没有被hack。
小收获:认真听取了讨论课上同学分享的思路和意见。学习了正则表达式,hashmap的用法。在格式正确判断上有明显改进,输出时没有优化sin^2+cos^2但是其余基本最简状态。
由上图看的出,第二次作业有了初步的依赖关系,并且各类之间分工明确,逻辑、作用清晰。将多项式送到PolyArray类,先进行rightPattern判断,成功后再进入cutString切割成一项一项,将每项送到singlePoly。其中进行模式识别,传值到Derivation,在该类构建hashmap,每次导数的结果加到hashmap中,最终将hashmap导入getMap到main函数中使用printMap打印。
Method | ev(G) | iv(G) | v(G) |
week.Derivation.addMap(String,BigInteger) | 1 | 2 | 2 |
week.Derivation.cloneMap() | 1 | 1 | 1 |
week.Derivation.der(BigInteger,BigInteger,BigInteger,BigInteger) | 1 | 4 | 4 |
week.PolyArray.cutString(String) | 1 | 3 | 3 |
week.PolyArray.rightPattern(String) | 4 | 3 | 4 |
week.SinglePoly.calPoly(String) | 1 | 14 | 18 |
week.SinglePoly.calVarTimes(String,Matcher) | 1 | 9 | 10 |
week.Week2.main(String[]) | 1 | 4 | 4 |
week.Week2.pg(int,BigInteger,BigInteger,BigInteger) | 1 | 9 | 9 |
week.Week2.printMap() | 1 | 13 | 13 |
week.Week2.readLine() | 2 | 2 | 2 |
week.Week2.select(BigInteger) | 3 | 3 | 4 |
Class | OCavg | WMC | |
week.Derivation | 2.33 | 7 | |
week.PolyArray | 3.5 | 7 | |
week.SinglePoly | 9.5 | 19 | |
week.Week2 | 5.4 | 27 | |
Package | v(G)avg | v(G)tot | |
week | 6.17 | 74 | |
Module | v(G)avg | v(G)tot | |
week2_Homework | 6.17 | 74 | |
Project | v(G)avg | v(G)tot | |
project | 6.17 | 74 |
由左边表格可以看出来尽管代码的复杂度仍然不低,相比第一次还是有一些改进。
那我们比较关心的就是类的OCavg,看的出值高的两个类,week2和singlePoly的圈复杂度较大,而圈复杂度与分支语句的个数很有关系。尝试改进singlePoly。
尝试了简单的重构,改变一点点正则表达式,添加xxPat中x在项头的情况,删去calPoly的一些内容,可以把OCavg降到7.0。再经过对于.calVarTimes的改造,将用的while isdigit手动判断带符号数变成了不断在特定start后寻找digitPat,OCavg降到6.0。再次改在calPoly,把对-号的判断移到新的方法calMinu中,可将OCavg降到4.0。
第三次作业:
小总结:因为生病,优化不完全,但是第三次还是学会了递归调用,完成了相对复杂的任务。
小收获:debug中构造测试树的概念,虽然这次没做好,但是对测试有更多的理解。
Method | ev(G) | iv(G) | v(G) |
week.Factor.findPolyBracket(char[],String) | 6 | 2 | 10 |
week.Factor.findTrigBracket(char[],String) | 6 | 2 | 10 |
week.Factor.isFactor(String) | 6 | 5 | 7 |
week.Mark.deriPolyArray(String) | 1 | 3 | 3 |
week.Mark.getpAry() | 1 | 1 | 1 |
week.Mark.gettAry() | 1 | 1 | 1 |
week.Mark.replaceBracketB(String) | 1 | 5 | 6 |
week.Mark.replaceTrigFunA(String) | 6 | 6 | 6 |
week.Methods.reVerse(String) | 1 | 6 | 8 |
week.PolyArray.cutStr(String) | 1 | 3 | 3 |
week.PolyArray.judgeForm(String) | 3 | 3 | 3 |
week.PolyArray.replaceBracket(String) | 8 | 7 | 9 |
week.PolyArray.replaceTrigFun(String) | 7 | 7 | 7 |
week.PolyArray.rightPattern(String) | 4 | 3 | 4 |
week.SinglePoly.calPoly(String) | 2 | 10 | 18 |
week.SinglePoly.deriA(String) | 4 | 10 | 15 |
week.SinglePoly.deriB(String) | 1 | 2 | 3 |
week.SinglePoly.deriIn(String) | 6 | 9 | 15 |
week.SinglePoly.deriX(String) | 3 | 5 | 8 |
week.Week3.main(String[]) | 1 | 5 | 5 |
week.Week3.readLine() | 2 | 2 | 2 |
Class | OCavg | WMC | |
week.Factor | 7.33 | 22 | |
week.Mark | 3.4 | 17 | |
week.Methods | 6 | 6 | |
week.PolyArray | 5.2 | 26 | |
week.SinglePoly | 8.4 | 42 | |
week.Week3 | 3.5 | 7 | |
Package | v(G)avg | v(G)tot | |
week | 6.86 | 144 | |
Module | v(G)avg | v(G)tot | |
Week3-Homework | 6.86 | 144 | |
Project | v(G)avg | v(G)tot | |
project | 6.86 | 144 |
可以由图表看出,虽然也有明显的递进层次,但是没有使用接口和继承类那样清晰。
由表格,这次之前没有复杂度的概念,作业3也没有做接口和继承的优化,复杂度上不尽人意。
(2)分析自己程序的bug
第一次作业:
样例分析:
第一次作业使用复杂度极高的字符状态机,在公测挂了几个点,在互测中也被hack了几次。而且是很明显,仔细测评一定能查出的错误。这次之后知道要花3小时左右列举各色案例,但还是没有发展出利用树来进行案例测试。
- 公测用例1:
+ -2192*x ^ -4723 - 1876*x^1298 + 3321 * x^ 0000 - +0000*x
错误输出:10352816*x^-4724-2435048*x^12970
正确输出:10352816*x^-4724-2435048*x^1297+0
原因:主要是打印错误,当打印系数为0项时,没有加+,只考虑了正数要打印‘+’。
- 公测用例2:
- 公测用例3:错误原因同用例2。
- 公测用例4:错误原因同用例2。
· --12345678*x^1+-87654321*x^1++19283746*x^1--47382846*x^1
+- x^ 0 --x^ +0 + 123*x^999- +66* x^0999-57 * x^ +999--7703*x^-39+-725* x ^ -
……
错误输出:-8642051x^998x^-40x^-91x^-57x^-51
正确输出:-8642051+0+0+0+0+0
原因:打印错误,当系数为0项时,根本不该打印该项只打+0即可。原先只是没有打印系数,却把后面的x^998等等内容打印出来。
以上公测用例错误都在方法main()中。
- 互测用例1:
· x^ + 1
错误原因:WF。因为使用字符状态机,为了压缩长度,将对空格和/t判断提前,使得^后不该有的符号数空白符被滤过了
- 互测用例2:特殊字符/v/f
错误原因:使用了trim(); 不知道trim()会去除所有空白字符。
以上互测错误都在方法rightFormat()中。
关联分析:
设计结构是纯粹线性的,而bug与其的相关性也很明显,就是一个线性不断剥丝的过程,无论是结构判断还是ArrayList输出,都是这种逐步淘汰错误模式的过程。
三个bug都是与该模式有关的,首先^后空格模式忽略就是状态机中一个状态疏忽。系数为0打印问题是个重要前提疏忽问题。而系数为0打印+也是一个个例疏忽。
结构问题在重构时会说明,测试会在测试bug时说明。
第二次作业:略。
第三次作业:
样例分析
公测和互测败在了同一个bug上。
- 公测用例1:
++(sin((x+x^2))+(x)*(cos((sin(x)+x^4))))-(sin((x+1))*(x+1))
错误输出:
cos((x+x^2))*(1+2*x^1)+(1)*(cos((sin(x)+x^4)))+
(x)*(-1*sin((sin(x)+x^4))*(cos(x)*(1)+4*x^3))-1*(cos((x+1))*(1+0))*(x+1)+sin((x+1))*(1+0)
正确输出:
((cos((x+x^2))*((1)+(2*x^1)))+((1))*(cos((sin(x)+x^4)))+
(x)*(((-1*sin((sin(x)+x^4))*((cos(x)*(1))+(4*x^3))))))-1*((cos((x+1))*((1)+0))*(x+1)+sin((x+1))*(((1)+0)))
原因:当-(sin((x+1))*(x+1))出现的时候,嵌套调用和一般 表达式调用导数的区别--需要括号区分!!!!!
一般表达式导数,每项只需要链式求导即可,最外圈无需括号
A*B’+A’*B -A*B’+A’*B
嵌套需要括号!否则-号出现,会使第一项后括号反向!
关联分析
很明显这次的结构较为紊乱。因此没有厘清所有的可能性。这就是为什么构建代码时要考虑圈复杂度,想这种圈复杂度太高,测试时需要将每条路径走多遍,就会造成bug层出,还不好检查的情况。debug超过五小时,无法找到上述bug。
同理,由分类树角度,这颗树没有一个很好的层次,枝丫太多杂乱。如果再写类似代码则需重构。
(3)分析自己发现别人程序bug所采用的策略
第一次作业:
- 1、首先讲自己的程序应对测试的设计问题以及自行测试时的问题:
- 有三个模式都没有测试到,^ + 1, 系数为0, 以及系数为0,且次数为1的情况。
- 这都是常见的模式,应该在测试时予以考虑。可以利用语义集。
- 举例:
- 空串
- 空格
- +
- *
- 0
- 5
- +5
- +-5
- + - 5
- 5*x
- - -5 * x
- - +5 * x ^ -9
- - +5 * x ^ - 9
- - +5 * x ^ - 9 + 8
- ……
- 可见这个测试集是很繁琐,且一个测试用例正确,不能说明某些类型正确。这就是一个无关联度,线性结构代码的问题,bug之间松散无联系,除了强行遍历模式很难完全查出错误。
- 2、再讲Hack别人的策略。第一次基本上是根据以上字符构造集遍历,考虑WF常见形式,这就能查出一些错误,比如空格,空串,有符号数中有空格,0无输出,正负号反了的错误等等。因为自己没有用正则,所以并没有着重观察正则的错误。
第二次作业:
hack的策略主要是观察对方代码,如果与自己雷同,则着重考虑自己曾犯过或可能犯的错误。不同,则考虑自己规避的错误,而对方潜在的。
步骤主要如下:
(1)观察理解正则表达式,先找笔误。 大家的测试时间可能不充裕,这时一个正则中的笔误则可能导致一些特定样例失误。
(2)观察判断条件即思路:
这次一个典型hack是对于“******”不显示WF的。
该同学的思路是反向构造,即创造6个错误的pattern,如果有,则WF。这种设计就很容易找到反例,可以依症hack。
(3)还有就是题意理解点偏差纠错。这次一个很特殊的点就是+++1是正确的,可以料想有些同学仍然按作业1判断,不妨一试。
第三次作业略。
(4)Applying Creational Pattern
因为第三次、第二次本身就是对前面版本的重构。所以我想应用对象创建模式来对第三次作业进行重构。
原先思路没有大的问题(暂不考虑优化)。但是可读性太差了,究其原因,是singlePoly一个类担当了多个层次的问题。
4个deri可以在单设的deri类。或者就是单独设A(函数类),B(括号嵌套类),xx(单纯x)三个类,
replace可以是A,B类中的方法,而replaceA,replaceB可以是A,B的子类extends出改写的方法。
这里只是提供一个重构的思路。这样,数据被划分好了类别和层次。调用起来就清晰方便多了。
总结
不多说了,继续努力!