OO第一次作业总结
第一单元的作业题目为实现表达式的求导,主要目的为初步建立面向对象的思想,熟悉java编程的语法,规范自己的代码风格(第一次用checkstyle)。客观来说作业的难度适中,层层递进,向我们展示了课程组的精益求精。对于我来说,难度逐步递进,但自己的面向对象思想并未进步,越来越回归面向过程式的编写,中间有过重构的想法也没有实现,这也就导致了自己三次作业越来越差。课程组向我证明了“试试就逝世”的代价有多么惨重,我也进行了深刻反思,保证以后的作业用好理论课的工具,进一步形成面向对象的思想。
一、基于度量分析程序结构
第一次作业
第一次作业只涉及普通多项式的求导,很简单,我建立了因子类(Factor),项类(Term),主函数(MainClass)相当于表达式类,这也就造成了第二次作业迭代的麻烦。在因子类中我把因子拆分成了系数(coe)、指数(exp)两大要素,看似在第一次作业中很成功,但是这种拆分元素的思想对我之后的两次作业造成了极大影响。类图如下。

问题一:Factor类中的getf方法是将项中的第一个因子可能带有的两个符号化简,这一步放进Factor类中是不合逻辑的;问题二:coe1、coeneg1、coeover0、other这四个方法都是用于output方法中的,本质上相当于将output拆分成了四个方法,目的是为了逃避checkstyle,显然这是不合理的,也看出了我并未真正按照面向对象的思想进行输出,应该是放在Factor类里变成tostring方法进行输出。

以上为oo度量(选取了标红的行),可以看出1.MainClass类复杂度高,不利于维护;2.other方法各项标红,说明复杂度高,也反映了output方法复杂度高,这都体现编程水平不足,面向对象思想的缺乏,细节处理不好。
第二次作业
第二次作业增加了三角函数的求导和括号的引入,也需要开始运用递归。我将表达式从MainClass类中抽离出来,建立了新的Expression类,除此之外没有别的类或者接口的建立,这里就体现出了我开始面向过程了。在因子类中我把因子拆分成了系数(coe)、指数(exp)、sin、cos四大要素,增加了判断括号的方法,递归的将表达式拆分成标准的四元组形式,这无不表现出我并没有运用面向对象的方法进行编程。我本应该进行一次重构,然后建立三角函数类、多项式类,并和因子有继承关系等,但我并没有这么做。我并未引入化简,害怕化简会出现爆栈的情况。虽然我的第二次作业依然还可以,但这导致了我第三次作业的失败。
。类图如下。

我在Term类中引入了plusterm方法,是将一个个拆分好的四元组重新进行乘法和加减法的运算,最后成为一个整体。这样的缺点是将括号全部打开,也就没法在求导的时候进行化简,只能求完导以后再化简,就增加了复杂度和运算时间。这本应该属于乘法的接口和加减法的接口的,说明我没有用面向对象的思想。还有Term类中各种tostring方法,也是为了checkstyle拆分的,如果有许多类的话就不必这样了。

以上为oo度量(选取了标红的行),可以看出行数大幅度增加,所有类都过于复杂,这就代表了我并未使用面向对象方法。
第三次作业
第三次作业增加了复合函数运算,增加了format检查。我仍然使用了上次的四个类,一个类和接口也没有加。在Factor类中我把因子拆分成了系数(coe)、指数(exp)、sin和对应的次数(cos同理)、sin\cos的复合函数的字符串五大要素,增加了判断format的方法,递归的将表达式拆分成标准的五元组形式,这无不表现出我并没有运用面向对象的方法进行编程。在Term类中我加入了sin和对应的次数、sin复合函数的字符串组成的hashmap(cos同理),使其一一对应。如果我第二次作业重构了,我只需要加入方法即可。我想过重构,但是又想试试这种面向过程的方法能不能将作业做出来,结果就“逝世”了。当然,我也并未使用“递归下降”的方法,也是想尝试直接用普通的匹配方法能否实现wrong format的判断,结果就因为这个“逝世”了。这件事告诉我,该学的就学,人是要进步的,不能一直用会的东西,要用新的知识解决问题。当然,这种将复合函数变为string的做法是无法引入化简的,复杂度会超高。因此,第三次作业崩了。
。类图如下。

我在Term类中引入了combine方法,依然是将一个个拆分好的元组重新进行乘法和加减法的运算,最后成为一个整体。这样的缺点是将括号全部打开,也就没法在求导的时候进行化简,只能求完导以后再化简,就增加了复杂度和运算时间。这本应该属于上次作业的乘法的接口和加减法的接口的,说明我没有用面向对象的思想。还有各种判断wrong format的正则样例和各种方法,无不有超高的复杂度,体现了我混乱的思维。


以上为oo度量(选取了标红的行),可以看出行数更大幅度增加,所有类都过于复杂,这就代表了我并未使用面向对象方法。
二、分析自己程序的bug
第一次和第二次作业都没有bug,第三次作业出现了强测wrong format判断的bug和输出字符串过长的bug,在互测中并未被发现bug。
第二次作业中并未拿到100分,是因为我面向过程式的算法将表达式全部拆分成元组形式,括号全部打开,而我并未进行三角函数的化简,只进行了同类项的合并;
第三次作业中wrong format一部分是因为我编程的马虎,在递归判断的时候所有expression二层递归直接返回true了;还有一部分是空格的问题,我算法是直接进行空格和制表符的删除,在这之前把带有空格的非法样例全部判断一遍,这种方法无法判断带符号整数中符号和整数分开的情况;
第三次作业中输出过长是重点问题,即我将表达式分成元组形式,其中复合的表达式我按照了string处理,这也就导致了在合并同类项的过程中需要进行字符串匹配,那么程序的运行会非常慢,一定会有TLE的情况出现,因此我删除了合并同类项方法,导致了输出过长。归其原因,还是没有使用面向对象的思想造成的。如果用了,绝对不会出现符合表达式是string的情况。
总的来说,我用自己的实践证明了面向过程的设计是不能在面向对象的编程中作为主体使用的,hashmap和arraylist是很好的容器,但是这不代表着我的所有东西都可以用这样的数据结构处理。接口和继承我根本没有使用过,这也是非常不好的。虽然我能完成前两次作业,也有着说得过去的成绩,但是第三次作业直接暴露了面向过程时复杂度高,无条理,各项之间十分混乱的缺点。
三、分析发现别人bug采用的策略
三次作业我都自己搭建了简单的评测机,对屋里的同学进行测试。具体方法即用python的subprocess开启子进程,将同学的java文件打成jar包进行测试。
在样例的生成中,第一次和第二次我都采用了正则表达式随机生成样例的方法,但是效果不佳。我认为主要原因是随机性过高,同质性样例太多,造成了效率很低。在第二次作业中我也采用了手搓样例的方法,针对特殊数据和结构构造特殊样例,测出了两位同学的bug。总结了之前两次的经验后,第三次我直接手搓样例,对于每条要求进行分析,构造样例,取得了一定的效果。
在结果的比对方面,我选择使用python的sympy库进行正确性的比对,使用人眼进行输出格式的正确性比对。缺点是应该自己构造比对输出格式的算法,这样既可以进行互测,又可以在自己进行wrong format判断时减少工作量。
四、重构
我没有进行过重构,只是在原有的基础上进行添加。我对我的错误进行分析,发现人一定不能懒,不能上头,不能不自量力地挑战。我认为自己不是不会用面向对象的方法进行构造,我也考虑过进行重构,已经进行过了思考,包括类、接口、函数在逻辑上和方法上的思考,但是我并未进行实现,固执地认为面向过程、元组拆分式的实现可以完成目标,但是失败了。我认为大家可以吸取我的经验,不要犯类似的错误。
五、心得体会
1.面向对象的思想十分重要,在处理这种现实问题的时候可以让自己的逻辑变得清晰,代码变得简单明了,更重要的是自己定义的类可以直接使用,就使得面向过程中“我要干这个,所以设计这个函数来实现”变成了“有一个东西,我要刻画他”。
2.一定要先前看,多学不是坏事,不要觉得自己会的一点东西就能吃遍天(更何况自己水平不行)。
3.大胆进行重构,不要一条道走到黑,不要懒,实在不行写一个面向过程的和一个面向对象的,对比来看会更有收获。
4.(小声建议)第二次作业可以弄得再复杂点,把我这种面向过程涉险过关的直接一棍子打死,第三次重构就下得了决心了,领悟也就会早一点。

浙公网安备 33010602011771号