【OO】第一单元作业-表达式求导总结分析
第一次作业
前言:作为之前完全没有接触过“面向对象”设计理念的新手小白,我的第一次作业直接采用了“面向过程”的设计,将C语言式的代码“翻译”成了java语言,整体功能难以扩展。
(1)基于度量来分析自己的程序结构
度量分析:在main方法中,我的基本复杂度,模块设计复杂度和圈复杂度都比较高。
第一次作业的结构设计不是很合理,将数据处理部分直接写在了main方法中,将wrong format输入判定直接放在了main方法中,没有设计成类或者方法,模块耦合度高,难以扩展维护。
(2)程序bug分析
第一次作业的强测和互测分别被hack了一个点。
错误分析:在写代码的时候,经过一波冷静分析,我坚定的认为当幂函数的指数为1时,系数不可能为1(例如当系数不为0时,指数为2的幂函数求导后系数肯定大于1)。
由于这波盲目分析没有考虑到合并同类项后系数可能变为1,而我的程序错误的输出了+*x,从而使得强测wa了一个点,互测被hack了一个点。
(留下了因为少写一行代码而悔不当初的泪水)
(3)发现别人程序bug所采用的策略
第一次作业的互测阶段,我构造了一个简单的测试数据集,分为错误输入和复杂数据,错误输入数据主要用来测试代码的输入处理是否正确(Wrong Format),复杂数据用来查看代码合并同类项是否正确,输出情况是否考虑完全。
第一次作业中,同屋的小伙伴有两个没有考虑除了空格和Tab之外的其他空格,统一使用了\\s来处理空白字符,导致了bug的出现。
还有一些同屋的bug是输出没有考虑完全(比如我的谜之思考逻辑),从而遇害。
(另外,由于第一次作业的hack情况是完全公开的,我在“测试”的时候是优先测试被hack过的人,也发现了一些bug。)
第二次作业
前言:相比较第一次作业,第二次作业加入了底数为sin(x)和cos(x)为底数的幂函数。我写第二次作业的代码也是完全按照第一次作业的思路,整体分为输入处理,数据处理,结果优化三部分。
(1)基于度量来分析自己的程序结构
度量分析:由于第二次作业延用了第一次作业的思路,整体还是2个类。可以看出,main方法和mOpt(优化)的三项指标都很高。由于优化方法是匆忙加上的,没有进行很好的设计,基本复杂度,模块设计复杂度和圈复杂度都很高。这次作业还是没有把main方法单独抽出来,导致main方法的耦合性也很高,难以维护和扩展。和上次作业相比,第二次作业的设计更为结构化,虽然一些方法的复杂度还是很高,但整体的设计较为清晰。
(2)程序bug分析
吸取了第一次作业的教训,我充分的考虑了第二次作业的输出处理,强测和互测都没有发现bug。
(3)发现别人程序bug所采用的策略
由于在写代码的过程中,我在测试自己的程序的时候也使用了大量的测试数据,构造了一个简单的测试数据集。
这次作业我测试的重点是正则表达式是否会爆栈,输入处理(例如sin和cos保留字之间的空白符问题),还有结果优化三部分。
同屋的Saber和Caster分别是爆栈和输入处理考虑不完全的,从而被害。
第三次作业
前言:在听到第三次作业可能会有嵌套数据的时候,经过一波理智分析,我单纯的认为嵌套肯定只会套一层,问题不大,不过是多处理一层的情况罢了。
而在下午收到指导书的时候,我冷静的看了一遍,,,,,,,,没看懂。
再看一遍,居然真的是多重嵌套,surprise!
这次作业基本脱离了前两次作业的思路,重构了一个新的程序。
(1)基于度量来分析自己的程序结构
度量分析:这次作业是完全重构的新版本。由于是自己手动for循环来判断输入的合法性,checkString方法的模块设计复杂度和圈复杂度都比较高,只能针对这次作业的输入情况来进行判定,模块耦合度高,可扩展性低。 setString方法是在进行了合法性判定后,对正确输入的数据进行一些基本的预处理(比如去掉多余的括号),基本复杂度和圈复杂度较高。
(2)程序bug分析
在放弃优化的情况下,这次作业强测没有被查出bug,当然除了WF外其余性能分都是0。
然而,在互测第一次数据更新后我就被hack了,透心凉,最终被hack了5次。
错误分析:在将表达式分裂成项的时候,我才用了for循环,将左括号压入栈中,在栈为空时遇到+-后就将之前的式子加入项的集合,而我考虑了‘+-’之前的字符不能为‘^’,没有考虑‘*’,项的分割出错导致了结果计算错误。
因为这次作业完成的比较赶,我没有对自己的程序做到而来充分的,覆盖性的测试,出现了这个相当严重的bug,在此真诚忏悔。
(3)发现别人程序bug所采用的策略
因为这次的代码量比较大,很难通过阅读所有人的代码来找bug,我写了一个超简单脚本,将所有人的输出放在一起进行比较,肉眼识别结果是否正确。
同屋的同学有的没有考虑sin和cos括号嵌套之间的空白符,还有的是幂函数的指数为10时不输出指数。
三次作业对比分析总结
输入处理:第一次作业采用大正则,直接进行整体表达式的判断;
第二次作业通过构造匹配器来进行项的单独小正则判断,防止爆栈;
第三次作业在我尝试了一周末的正则匹配失败后,最终采用了手动for循环进行小表达式的正则匹配。
数据处理:第一次作业和第二次作业都是直接进行计算,将项的结构存储在arraylist中,统一进行输出判断。
第三次作业则是采用了递归下降进行表达式的处理,在得到一项求导结果后立即加入结果字符串。
输出判断:前两次作业都尝试了一些简单的优化,合并同类项后输出结果;
对于第三次作业,我选择放弃。最终只是也进行了一些简单的符号替换。
对比三次作业,我学到了很多,代码结构逐渐变得清晰了,(可能)摸到了所谓“面向对象”设计理念的门槛,逐渐将基本的功能实现都交给方法去做,抽象成类,实现相应的代码分装,提高了代码可维护性和扩展性。