I used to be enamored of object-oriented programming. I'm now finding myself leaning toward believing that it is a plot designed to destroy joy.
一、基于度量来分析程序结构
总体:
第一次作业:在两个类中处理了所有多项式的求导,一个主函数控制输入并且处理字符串,格式不正确时报错;另外一个处理多项式并且求导。
第二次作业:在第一次作业的基础上讲多项式和单项式分离开来,由于每一项可以简化成(k,a,b,c)的函数,所以没有区分三角函数与单项式。
第三次作业:对第二次作业进行了重构,但依然没有使用继承与接口,将字符串处理完毕后输入Nest类分离加减项,再传入另一个类分离乘除项,分别有各自的求导方法,符合单项式或者单项三角函数或者数字为止。
基于量类的属性个数、方法个数、每个方法规模、每个方法的控制分支数目、类总代码规模,并计算了经典的OO度量,分析类的内聚和相互间的耦合情况,具体指标如下。
第一次作业:
类图:一个Main莽完所有……
度量分析:
可以看出,第一次作业中使用的类很少,并且使用了内部类,没有分离出来。一个main莽完了所有类,很多方法复杂度过高。对于字符串的处理采用了判断一项然后删除的方法,每一项分为了三个(常数,省略项,或者普通项),正则匹配的时候用了诸多if-else语句,增加了复杂度。求导化简时只是合并了同类项,对于正负的加减号没有进行有效的化简导致出错。因为只用了一个类,嵌套深度比较深。
第二次作业:
第二次作业在第一次的基础上做出了修改,因为每个单项可以化简成一个四元函数,于是抽象出两个单项式和多项式的类,分别有求导的方法。不足之处在于可扩展性很差,给第三次作业带来了很大的难度;此外,因为水平有限,除了合并同类项,对于三角函数繁多的化简也没有研究;最后,第一次作业中讲多项式处理输入也没有抽象出一个类,导致main里面有挤满了一堆代码。
第三次作业:
第三次作业对第二次作业进行了重构,因为对继承和借口的理解仍然有偏差,所以并没有运用到继承和借口。将字符串处理抽象出了一个input_handler的类,然后对于最基本的项建立了number和polyterm的类。字符串处理完毕之后输入Nest和Factor的类,两个类求导方法中反复迭代直到最后if-else判断是符合单项,再传入number和polyterm的求导。由于在类之间,多项式及其结果是以字符串的形式传递,化简起来比较麻烦,所以没有做相应的化简。
Factor和Nest的diff方法都有若干个if-else语句,复杂度和嵌套深度都很高。
二、BUG部分
主要分为格式错误的判断和输出结果两部分。
格式错误:
(1) 出现了要求之外的非法字符,比如\f \v:
对于字符串预处理的时候可以先判断是否含有规定字符之外的字符串。
当时偷懒用了\s识别所有的空格和\t,结果没有注意到其与空格差别,踩了坑。
(2)因子作为嵌套项时没有括号:
指导书上很明确的写了(然而第一遍我并没有认真读,我滚了
输出的时候也要注意在+-因子外面加上括号,为了保险起见我一股脑的括号全加上了,导致输出特别长,后来想想,两个基项和乘除项的括号应该可以省略。
(3)去括号时注意括号是否匹配:
最开始去掉不必要的括号时选择直接while循环去掉最外层的括号,但是形如(3*3)+(4*sin(x))就报错WF,发现去掉括号时需要判断是否匹配,于是加了个flag标志是否匹配判断。
(4)加减号重复:
项与项之间有符号,项本身还有符号,就可能造成多余的输出。最后replaceAll一下就好了。
输出错误:
(1)乘以1或者0的项
如果乘以0,当开始写的时候就不输出了,结果只有一个项的时候结果就比较尴尬;1也是类似的问题,自身再化简就容易输出格式错误。现在也没太想好怎么处理……
(2)略项前面的符号
省略项只能出现在首项中,否则就是格式错误……还是选择加括号好了……
等等等等。
如何构造测试样例
讨论课上看神仙们分享了自己写的对拍器,觉得很有实践价值与意义。我自己测试自己的时候仍然停留在针对能想出的错误类型,构造测试样例,然后输入IDLE用Python自带的包,随机选取数字,看看结果是否一致。
三、Applying Creational Pattern
面向对象的思想就应该是由简到繁,层层的递进,将所有要实现的功能抽象为类,而不是第一步该干什么,第二步该干什么,否则在多项式输入的时候WF的判断细节将会变得十分复杂。在每一次实践中我都在不断加深对面向对象的几大原则的理解……
单一职责原则,是指一个类的功能要单一。比如在第二次作业中,所有项都归结为 k*x^a*sin^b*cos^c形式,可以省时省力但可扩展性差。其次, 要遵循接口分离原则和继承接口, 模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来。在第三次作业中观摩了其他小伙伴的作业,才恍然大悟求导可以提取出来作为一个接口,这样可以省去对于各个单项的判断。最后通过循环依赖,不停地讲表达式缩减(类似于表达式树),实现递归求导。
四、 总结
Java基础几乎为零,也缺乏实践指导的情况下,应该先熟悉基本编程思想,再纠结编程技巧……事实证明,一个坏的设计架构带来的麻烦不止一点,不将多种功能分离开而想一口气吃成个胖子的后果就是debug难度直线上升。
最大的感受就是,学好正则表达式,有秩序的建立类,少用ifelse,少用ifelse,少用ifelse。
QAQQAQQAQ我一定重新做人,哭了(x