OO第一次作业总结
经过三次OO作业的练习,积累了一些知识,也总结了一些经验。三次作业是递进式的求导编程练习,从第一次作业单一的幂函数求导,到第二次作业加上三角函数求导,再到第三次作业复合函数嵌套以及表达式因子综合的求导练习,思维难度不断加大。
一、基于度量来分析自己的程序结构
第一次作业
第一次作业要求对简单的幂函数进行求导,其思路为用正则表达式对格式的合法性进行匹配,而后用group方法进行拆分处理,再对每一个项进行求导,最后组合相加即可。
在这其中建立了类。其视图如下:
有两个类,一个是为单一项,储存系数和指数;另一个是多项式,以hashmap的方式储存每一项。
其度量结果如下:
可以看出创建多项式的过程复杂度较高,因为其多次调用单项的构造,需要完整的将表达式转化完全才结束。
还有输出计算结果的复杂性较高,需要考虑多种情况导致的。
第二次作业
第二次作业在第一次作业的基础上增加了三角函数部分,只需要在第一次作业的基础上进行修改即可,将单一类增加两个指数部分代表cos和sin的指数,其他实现思路类似。需要说明的是求导采用的是公式求导,即将通项公式写出,并未采用递归写法或者表达式树的构建,而这也为第三次作业的大修改埋下伏笔。
具体类图如下:
可以很清晰的看出和第一次的实现思路是类似的,这样实现比较清晰且可以将其模块化,在什么样的阶段做什么事情。不足之处是将mian函数放在一个类中是不合适的,以及部分方法的行数较长,虽然在60行以内,但长度仍然没有控制好。
下面给出度量结果
可以看到并没有控制好复杂度,其原因在于对面向对象的思维并未良好掌握,采用面向过程的思路去写,导致在一个方法中实现了很多东西。
CreatMultinomial.add1(Map,int) | 1.0 | 3.0 | 3.0 |
CreatMultinomial.addtoMap(Map,TriClass,BigInteger) | 1.0 | 2.0 | 2.0 |
CreatMultinomial.beforeprint(CreatMultinomial) | 1.0 | 3.0 | 3.0 |
CreatMultinomial.calculate(Map) | 1.0 | 5.0 | 5.0 |
CreatMultinomial.creat(Map) | 1.0 | 6.0 | 6.0 |
CreatMultinomial.creatAndJudge(Map) | 1.0 | 3.0 | 3.0 |
CreatMultinomial.CreatMultinomial(String) | 1.0 | 3.0 | 3.0 |
CreatMultinomial.getMap() | 1.0 | 1.0 | 1.0 |
CreatMultinomial.isokay(String) | 1.0 | 1.0 | 1.0 |
CreatMultinomial.print(Map) | 1.0 | 13.0 | 13.0 |
CreatMultinomial.setMap(Map) | 1.0 | 1.0 | 1.0 |
CreatMultinomial.together1(CreatMultinomial) | 1.0 | 10.0 | 10.0 |
CreatMultinomial.together2(CreatMultinomial) | 1.0 | 10.0 | 10.0 |
CreatMultinomial.togetherAdd1(TriClass) | 1.0 | 5.0 | 5.0 |
CreatMultinomial.togetherAdd2(TriClass) | 1.0 | 4.0 | 4.0 |
CreatMultinomial.togetherSelet1(Map,Map,Map,TriClass,int) | 6.0 | 6.0 | 6.0 |
Main.main(String[]) | 1.0 | 4.0 | 5.0 |
SingleElem.getCoefficient() | 1.0 | 1.0 | 1.0 |
SingleElem.getTriClass() | 1.0 | 1.0 | 1.0 |
SingleElem.saperate(String) | 1.0 | 1.0 | 1.0 |
SingleElem.seperateCofficient(String[],int) | 1.0 | 5.0 | 5.0 |
SingleElem.seperatecosIndex(String[],int) | 1.0 | 11.0 | 11.0 |
SingleElem.seperatesinIndex(String[],int) | 1.0 | 11.0 | 11.0 |
SingleElem.seperatexIndex(String[],int) | 1.0 | 11.0 | 11.0 |
SingleElem.SingleElem(BigInteger,BigInteger,BigInteger,BigInteger) | 1.0 | 1.0 | 1.0 |
TriClass.equals(Object) | 1.0 | 4.0 | 4.0 |
TriClass.getCosIndex() | 1.0 | 1.0 | 1.0 |
TriClass.getSinIndex() | 1.0 | 1.0 | 1.0 |
TriClass.getXindex() | 1.0 | 1.0 | 1.0 |
TriClass.hashCode() | 1.0 | 1.0 | 1.0 |
TriClass.print() | 1.0 | 11.0 | 11.0 |
TriClass.setCosIndex(BigInteger) | 1.0 | 1.0 | 1.0 |
TriClass.setSinIndex(BigInteger) | 1.0 | 1.0 | 1.0 |
TriClass.setXindex(BigInteger) | 1.0 | 1.0 | 1.0 |
TriClass.TriClass(BigInteger,BigInteger,BigInteger) | 1.0 | 1.0 | 1.0 |
Total | 40.0 | 145.0 | 146.0 |
Average | 1.1428571428571428 | 4.142857142857143 | 4.171428571428572 |
可见,在一些字符计算处理上过于冗杂,导致复杂度上升。
第三次作业
第三次作业加上了复合函数,嵌套和表达式因子等,导致前两次作业的正则很难直接用在第三次作业中,因为很难对合法性进行判断,且拆分项也较困难。
在这种情况下,经过和同学的讨论,我们实现了一种递归分析的方法,具体而言是将每一次括号内的东西替换为特殊字符,而后即可使用第二次的正则表达式进行分析,这种分析是递归的。
同样,可以将上述正则拆分的项分离开来,建立类。
下面详细描述一下类的建立:首先建立多项式类(expression),其中有一个ArrayList属性,其中存储的是每一项(term),其构造方法为将输入的字符串传入其中,构造n个term,将term加入到ArrayList中,term的构造即将由上面拆分的字符串构建,其也具有一个ArrayList,其中存储的是因子(factor)。factor里面的属性不再是ArrayList,而是一个字符串,就是term传入的字符串以乘号分割的结果,于是分成了多个factor,多个factor加入到一个term的ArrayList中,成为一个term,多个term加入到expression的ArrayList中,形成一个expression。由此自顶向下,构建出完整的结构链。
关于求导:每一个类都有一个求导方法,对于expression,其求导方法即为调用每个term的求导,而后以加减号链接即可。对于term的求导,其求导规则为乘法求导规则,因此写一个循环即可,分别调用factor的求导。对于factor的求导则分两种,一种为普通因子的求导(普通三角函数,普通幂函数,常数),而对于特殊因子,分两种,一种为expression-factor,一种为嵌套复合。对于第一种,直接调用expression求导方法即可,对于第二种,是递归求导,它的模板应该是 sin(factor)^d,直接改成cos再乘factor的求导即可。
所有的求导结果均返回字符串。
下面给出类图:
经过上述的描述,类图的功能应该较清楚,故不再进行详细叙述。
下面给出复杂度分析:
其复杂度并未与第二次作业相比增加,反而有所减少,说明对于如何写出高效程序有所进步,由于引用了第二次作业中的部分代码,因此复杂度并未彻底下降,在第三次作业重构部分表现较好。
二、自己程序bug分析
这三次作业做得一般,其中有两次强测得分都不高。第一次作业,实现思路很好,优化也很到位,但是忘记了用biginteger类,只是将数据的范围限定在int类,使得强测与big类有关的数据全部报错,经过一次性的bug修复后即解决。这说明一个简单的小问题,可能会造成巨大的bug,使得陷入十分麻烦的境地,因此在写程序的时候需要十分慎重,不能有很严重的闪失。
第二次作业做得不错,强测点全部通过了,但在优化的地方将一个hashmap 的序号写错,以至于在互测的时候被hack到了这个点,这说明平时写程序的时候没有专注,有一些地方并没有按照自己的想法去做,但由于自己没有耐心的构造数据去测验,以至于出现纰漏。
第三次作业做得不好,也是因为一个小bug,将.equals方法用成了等号,以至于出现了严重的bug。
在三次作业中,均由于一些小小的问题,产生很严重的后果,这警醒自己写程序时候要多构造测试点,同时不能忽略掉idea所给出的warning,事实上第三次作业可以很轻易的规避掉,而后强测便不会出错,互测也不会出现很大的问题。因此需要警示自己。
三、互测分析
互测不是我所擅长的东西,在两次互测中,我都只提交了一个bug,采用佛系找bug的方式,去做黑盒测试。我会针对性的写一些,例如爆栈,例如tle的bug,对于其具体的一些bug,则不太擅长发现,因此还需要和同学多多交流,也希望可以收获到好的找bug方式。
当然我认为找bug是一个很费精神的事情,所以没有必要为了bug去强行hack他人,如果找得到固然很好,找不到也不必要为此烦忧。
四、Applying Creational Pattern
这三次作业的构建模式,前两次采用面向对象的形式,但用的其实是面向过程的思路,将事件过程化,而后对每个过程建立方法和类,当然也用到了一些面向对象的知识。
第三次作业采用更多的面向对象思想,建立了富有层次的类,并给出了层次性方法,比较遗憾的是还没有用到继承,抽象,接口,在重构时候可以考虑将三者的求导方法都抽象成接口,等等。