OO第一单元 多项式求导 总结回顾

写在前面

  从完全没有接触过java,到完成了OO第一单元的三次作业,确实经历了很多曲折。然而每次做完作业带来的成就感,是其他课程难以比拟的。在三次互测的过程中,也学到了很多知识,此处做一个简单的总结。

 

(1)基于度量来分析自己程序的结构

第一次作业

UML图

OO第一单元 多项式求导 总结回顾_第1张图片

 

代码行数

耦合度分析
OO第一单元 多项式求导 总结回顾_第2张图片

 OO第一单元 多项式求导 总结回顾_第3张图片

 

  可以清楚地看到,OO第一次作业里,我并没有面向对象的意识,虽然写了Poly类,但实际上依旧是面向过程的编程,MainClass类仅仅调用了poly类里的几个函数,依旧是换汤不换药的一main到底。虽然第一次作业题目比较简单,但混乱的代码结构、完全不面向对象的代码,使得第一次作业的耦合度出现了红色。代码也几乎没有任何一点拓展性,也导致我第二次作业必须重构,耗费了很多时间。这也是编程时没有充分考虑导致的,应当吸取教训。

 

第二次作业

UML图
OO第一单元 多项式求导 总结回顾_第4张图片

代码行数
OO第一单元 多项式求导 总结回顾_第5张图片

耦合度分析
OO第一单元 多项式求导 总结回顾_第6张图片

 OO第一单元 多项式求导 总结回顾_第7张图片

OO第一单元 多项式求导 总结回顾_第8张图片

 

  由于第一次作业的不可扩展性,我重构了代码,开始尝试使用面向对象的思想来编程,并且引入了工厂模式来创建对象。由于希望尽量为第三次作业留下扩展的空间,代码写的相对规整,但是程序的耦合度依旧居高不下。

 

第三次作业

UML图
OO第一单元 多项式求导 总结回顾_第9张图片

代码行数
OO第一单元 多项式求导 总结回顾_第10张图片

耦合度分析
OO第一单元 多项式求导 总结回顾_第11张图片

 OO第一单元 多项式求导 总结回顾_第12张图片

OO第一单元 多项式求导 总结回顾_第13张图片

OO第一单元 多项式求导 总结回顾_第14张图片


  直到现在我才惊奇地发现我第三次作业的代码竟然比自己第二次作业的代码要少近80行.....且第三次作业代码行数大部分集中在解析读入的表达式部分,占了近乎250行。这主要是因为我将判断字符串的合法性以及根据表达式匹配创建相应的的对象这两个部分分开来写,先判断了是否合法才进行解析。但其实这两部分的内容是很相似,特别是匹配的正则表达式几乎一模一样,完全可以合在一起,使代码更加简洁高效。由于思路比第二次清晰了很多,虽然多了一些递归调用,但代码的耦合性、复杂度并没有上升(不过形式依旧不容乐观)。

 

(2)程序的bug

  第一二次作业中测、强测、互测均为出现bug,也并没有被bug困扰很久。可以说过得比较顺利。在做第二次作业前,一度写出replace("x\*\*1","x")这种低级错误的句子,误将x**100也替换了x00,所幸在测试自己程序的时候发现了这个错误,最后结合正则表达式的(?!)反向预测先行搜索的表达式解决了这个问题。
  第三次作业中debug的过程就比较艰辛,虽然最终互测未被测出bug,强测有一个点未通过。但在写代码的过程中发现了无数的问题。大部分bug都能通过调试很快解决,印象也并不深刻,多是一些低级的错误。只有三个bug留下了很深的阴影。

1.违规空白符的判断

  这个bug也是导致我强测有一个点未通过的原因,由于继承了第二次作业的思想,我希望在匹配表达式以前将所有的空格和制表符都删除,以此来降低正则表达式的复杂度,故我写了一个方法来检测是否有违规出现的空白符。在检测时,考虑了**之间的空白符,sin、cos和整数之内的空白符,指数上+-号与正整数之间的空白符,以及三个+-号和正整数之间的空白符几种情况,若有误则输出WF,无误则repalceAll("[ |\t]", "")。天真地以为自己遍历了所有空白符位置违规的情况,在编写测试数据的时候,也掉入了思维误区,忽视了表达式因子内的情况,强测中cos(-   1)这个数据便未能通过。这也是自己读指导书不认真、考虑违规情况时没有按照一定的规律和逻辑导致的。吸取经验教训,在考虑这些特殊情况以及编写测试数据时,要按照一定的逻辑、有规律全面的遍历,不能想到什么写什么,很容易遗漏。

 

2.嵌套的表达式因子中-cos(x)省略1\*,导致未能匹配符号

  这是中测的第五个点的错误,当时我在这个点纠结了很久,由于没有给出输入数据,面对茫然的bug只能对着空气硬着头皮de,很自信的以为自己的嵌套和递归写得很好,bug不会出现在嵌套那个地方。结果耗费了很漫长的时间,最后由于运气很好,在胡乱输着测试数据时惊奇地发现了这个bug,虽然解决这个Bug很容易,但是定位它的过程却很心酸。也吸取教训,在debug的时候不能依靠思维惯性,不能想当然,不要觉得应该没问题就跳过,应该全面地从头审视代码和思路。

 

3.TLE超时

  在提交前我测试了(((((((((((((((((((((((((((((x)))))))))))))))))))))))))))))这个数据,合法但是有着恐怖的嵌套,本来以为我的程序可能会超时,没想到电脑直接卡死了,风扇狂转,根本无法得出结果。我一度十分绝望,以为是自己的代码写得太垃圾、太复杂,也不知从何改起。正准备重构时,与一位朋友的讨论帮助我发现了问题所在,超时只是由于一个细节决定的:
OO第一单元 多项式求导 总结回顾_第15张图片

  我在求导时,在if判断语句里递归调用了求导函数,在if的具体执行内容里又再次递归调用。这样的操作让代码的复杂度直线上升,导致超时。最终我建立了一个temp变量来保存结果,一开始就求导且只求导一次,将结果保存在temp里,修改以后便能秒出答案。没想到这样几句话就能让我的代码运行时间暴增十倍。

OO第一单元 多项式求导 总结回顾_第16张图片

 

 

 

(3)发现别人的bug时采用的策略

  由于觉得互测的本意就是让我们相互学习代码,故没有依赖自动评测机(最主要是也不会写好的评测机,哭了)。互测时,我首先将自己编写的测试数据输入进行盲测,如果发现了问题,再通过调试来看那位同学出bug的原因。不过由于自己编写的测试数据数量有限,且覆盖面不广,故一般只能发现三四个bug,运气差的时候一个bug也找不出。最后只能通过阅读同学的代码,根据逻辑思路来判断。但是,同学们的bug一般都是出现在细节的地方,而细节部分自己又很难读懂,阅读别人的代码也很吃力。因此三次作业我都只hack了四五次,效率很低。希望以后的作业里自己能有所改进吧。

 

(4)应用对象创建模式

  三次作业里,第一二次作业都应该按照第三次作业的思想来进行。因为只有第三次作业里我才真正有了一点点面向对象的思想,前两次作业拿到指导书以后,我都是按照“怎么解析输入——怎么求导——怎么输出”的思路进行的,这导致前两次作业我还是在拿着面向过程的思路来完成,不仅代码复杂,可扩展性也很低。

  另外,工厂模式是我一直需要改进的点,自己的工厂模式写得并不好,在完成了第二次实验后我才意识到这一点,工厂模式应该具有很好的可扩展性和简洁性。

  第三次作业我依旧是对几种因子、项、表达式建类,并分别在各个类下重写求导方法。如果要对第三次作业进行重构的话,我认为可以对加减运算、乘运算、嵌套运算进行建类,对表达式解析进行建树,利用树形结构来解决问题。我觉得这是一个很好的思路。

 

(5)对比和心得体会

  和四位大佬的代码比起来,我的程序有着非常多的问题与不足。我认为最大的差距在于,我的代码对于面向对象的思想贯彻得还不够,依旧停留在面向过程的层面。这就导致每次作业我几乎都要重构代码,而且代码的可扩展性、可读性都很弱。只过了一个星期,我甚至都不能看看懂自己之前写的代码,很多地方写得非常复杂和繁琐。这主要是由于自己的代码并没有一个合理的设计。在对表达式的解析、输出和化简,都可以建立一个类来解决问题。反观四位同学的代码,看上去一目了然,思路清晰,对类的构建很合理,可扩展性强,几位同学对工厂模式的应用也很熟练,甚至对表达式做了很多化简,这些都是很值得我学习的。

 

  作为一个几乎没接触过java的小白,在做第一单元的作业过程中确实是很痛苦的。在做作业的过程中也会吐槽题目的难度和复杂,第三次作业在找中测第五个点的Bug时也体会过深深的绝望,但三次作业做完以后,能给我带来很大的成就感,每次看到自己的代码AC,互测时看到自己没被hack,都能开心好一会。回过头来看,发现自己也有在一天内写好一个1000行左右代码的能力。在做三次作业的过程中,有过很大的收获,也能真切地感受到自己的进步。希望自己能在面对未来的作业时,更加地从容不迫!

你可能感兴趣的:(OO第一单元 多项式求导 总结回顾)