表达式求导系列——OO作业第一弹
一、三次作业的分析与反思
Homework One
1.设计思路
实话说,在拿到第一次作业的时候,刚刚稀里糊涂写完Pre的我,还完全不知道面向对象思路设计是个什么玩意儿,所以拿到题目之后基本上就直接上手硬刚了……
至于硬刚的结果嘛,现在看起来真的是惨不忍睹了,大致内容如下
- 预处理:这次最简单无脑的预处理我竟然没有集中进行,而是一边读入一边用trim()进行处理
(yysy愚蠢至极) - 输入处理:通过一个输入类Input读入字符串,并在类的内部通过两个方法分别构建表达式和项
- 内容构建:分为了表达式类Poly和项类Term两层,在Term中完成了化简,但是类的实例化都是在Input类中进行的,Poly类有点儿形同虚设
- 求导计算:通过一个单独的求导类Compute进行(起这个名字是因为我当时没想到会一直求导,我还以为会要增加其他功能……)
- 输出处理:在主类Main中进行,直接调用了Term中的方法,输出的同时完成化简
2.结构分析
HW1的UML图如下:

HW1的复杂度分析如下:




3.bug分析
- 这次作业由于比较简单,我在中测通过之后也有大量时间进行自我测试,应该大致实现了情况的全面覆盖,也全部保证了正确性,所以最后也顺利通过了强测和互测
(ohhhhhhhhhhhhh),但也就这一次都过了,后两次作业还是被hack爆了……
Homework Two
1.设计思路
经过第一次的作业(也是因为题目难度的上升),我开始逐渐领悟到设计的重要性,所以这次作业我并没有第一时间开始上手,而是先整理了一下思路,结果大致如下:
- 格式判断:为了增强代码的可扩展性(这次终于预判对方向了),我依据指导书上的内容用正则表达式对输入进行了格式判断
- 预处理:这次我吸取了第一次的教训,在构建表达式之前进行了预处理,去掉了所有空白字符,并把指数符号进行了替换
- 内容构建:在表达式构建的部分,我也作出了一定的改变,因为这次作业中,常数因子、幂函数和三角函数都只有一个需要储存的参数,所以我就没给它们再各自构建类,而是都储存在了Term类中(当时我也发觉好像这样很不具扩展性,但对于化简来说是真的便利)
- 求导及化简:因为我选择了上述构建方式,求导和化简都变得十分简单了,在这也不多说了
- 输出处理:这方面我基本上延续了第一次作业的处理,也不再赘述
2.结构分析
HW2的UML图如下:

HW2的复杂度分析如下:






3.bug分析
- 很不幸,这次被hack爆了
(血海深仇1.0)。被强测和互测出的bug有两个,分别是一个WRONG FORMAT的误判和漏写了一个break…… - 对于输入格式误判的bug,是存在设计问题;对于另一个bug,大概是测试数据不够的问题了
Homework Three
1.设计思路
因为这次作业整体难度可以说是远大于前两次,复杂度相应也比较高,设计就变得尤为重要了。我这次设计结构的时间也明显变长了很多,期间修修改改,最后大概得到了如下的处理方式:
- 格式判断:实话说,由于我确实没有想清楚这一部分如何处理,我最后用了一种极度愚蠢的方法(不过看了其他同学的做法之后,我现在倒是大概明白了)。整体上我还是延续了正则表达式的方法,只不过添加了很多重复的内容,代码非常繁复冗杂……
- 预处理:这次预处理基本延续了上次的做法,去掉空白字符的同时对表达式做出了一定调整,从而方便之后的分割操作。不过不同的是,为了实现类似递归循环的操作,我在Expression, Factor, Trigonometric三个类中都对括号进行了一定处理,从而方便正则表达式的使用。
- 内容构建:整体上内容构建的代码结构还算比较清晰,简单来说,分为表达式、项、因子三个层次,其中因子层可以定义表达式。但由于这样循环定义结构的存在,代码的复杂度也比较高,最后也导致出现了bug。
- 求导计算:这次的求导我觉得自己写的不是很好,简单来说就是太过分散,需要一层层去传递信息,而且本身的结构就是一个递归循环的结构,求起导来就显得比较乱,debug也不是很方便。
- 化简:和前两次相同,这次我在最开始设计的时候并没有设计化简步骤,但由于这次代码的复杂度急剧上升,导致我在完成基本功能之后很难再大幅修改代码从而实现化简。这也反映出我代码的扩展性真的是差的离谱,稍加改动就容易出bug。
- 输出处理:输出处理和求导相似,都需要层层传递信息,结构有些复杂,最后由于没有实现化简,冗余的内容也很多。
实话说,这次作业刚写完的时候我心里就没什么底,后来也果然出了bug。
2.结构分析
HW3的UML图如下:

具体的复杂度分析如下:






3.bug分析
- 这次作业我在强测和互测中各被发现了一个bug
(但竟然在一个点被hack了26次,血海深仇2.0)。 - 强测中发现的是一个WRONG FORMAT漏判的问题,互测则被hack到了一个很显而易见的点,简单来说就是在处理括号的时候少考虑了括号外的正负号问题。
二、hack策略
- 经过三次紧张刺激的互测,同学之间已经形成了
血海深仇深切的革命友谊,为了更好的评测自己的代码、体现TDD思想,顺便得到更多互测分数,如何设计测试样例就成为了一个不可忽视的问题。 - 在过去的三次作业中,我的做法比较统一,即在完成全部代码并通过中测样例后,手动出样例。这样的做法可以说是全无TDD思想,表面节省了一部分时间,但其实只是把思考放在了后面,甚至还会白白搭进去debug的时间。手动出测试样例的方式虽然不如随机样例数据量大,覆盖面可能也不那么全面,但在设计样例的过程中可以让自己加深对于题目的思考,倒也不算白费功夫,日后可以考虑延续。
- 在之后的作业中,对于测试样例的设计,我目前有以下设想:
- 拿到题目并理解题目要求之后,第一时间设计测试样例
- 设计时分类考虑,比如从输入格式判断、基本功能实现、组合功能实现、化简功能评估等角度分别进行设计
- 设计代码布局的时候结合样例进行思考,划分功能
- 在中测结束前完成所有设计代码的测试,并总结此次样例设计的经验
坐等互测分在互测中和屋内同学进行深切友好的测试样例交流
三、关于工厂模式
- 之所以单独把工厂模式拎出来,主要是我觉得这个设计模式在作业中确实有很强的应用价值,可以在一定程度上将原有代码结构进行简化,分散单个类的功能,降低类之间的耦合度。
- 在第二次实验课和第二次研讨课中,都介绍了工厂模式,整体来说,这一模式给我留下最深的印象就是极致的封装和透明化。将创建内容透明化,隐去具体创建操作的细节,而只留下创建的接口供用户使用。而简单工厂、工厂方法模式,乃至抽象工厂,更是逐步递进,从具体类别的封装到工厂的封装,这对于我之后的作业也很有借鉴意义。即使是原来的作业,也可以考虑重构从而实现代码的简化。
四、心得体会
- 经过第一单元三次作业的练习,感觉现在自己对于面向对象的思想好像还是领悟的不太深入,但应该算是找到了一个大方向,简单来说就是尽可能根据功能进行结构上的封装,从而降低代码的耦合度,增强扩展性和可移植性。
- 具体到今后的作业,我觉得自己有必要在设计阶段投入更多精力,特别是增强TDD思想的应用,先构造测试样例,既可以让自己更快更好的理解题意,又能够让自己的代码设计更为全面
,还能刀更多人,确实不失为一种好策略。