本单元共有三次作业,分别为:
HW1:实现对仅包含简单幂函数的多项式的导函数求解。
HW2:实现对包含简单幂函数和简单三角函数的多项式的导函数求解。
HW3:实现对包含简单幂函数、简单三角函数、复合函数的多项式的导函数求解。
一、code analysis
先回顾一下三次作业的UML类图和Complexity metrics。
HW1
HW2
HW3
HW1
class | OCavg | WMC |
---|---|---|
Poly | (color{red}{5.67}) | 17 |
Term | 2.00 | 10 |
MainClass | 1.00 | 1 |
Total | 28 | |
Average | 3.11 | 9.33 |
HW2
class | OCavg | WMC |
---|---|---|
Poly | (color{red}{4.75}) | 19 |
Term | (color{red}{3.73}) | (color{red}{56}) |
TypeX | 2.25 | 9 |
TypeSinx | 1.75 | 7 |
TypeCosx | 1.75 | 7 |
Regex | 1.00 | 9 |
MainClass | 1.00 | 1 |
Total | 108 | |
Average | 2.63 | 15.43 |
HW3
class | OCavg | WMC |
---|---|---|
Poly | (color{red}{5.42}) | (color{red}{65}) |
Term | (color{red}{4.64}) | (color{red}{65}) |
TypeX | 2.20 | 11 |
TypeSinx | 2.20 | 11 |
TypeCosx | 2.20 | 11 |
Regex | 1.00 | 14 |
MainClass | 1.00 | 1 |
Total | 178 | |
Average | 3.18 | 25.43 |
可以看到,三次作业下来,代码的耦合度基本处于直线增长的情况,程序变得越来越臃肿。究其原因,是自己不愿试错和探索,始终没有尝试使用低耦合的设计模式,如工厂模式,而是把很多面向过程的思路在三周内从头贯彻到尾。
HW1中,要实现的功能不多,程序勉强还算能看。
自HW2起,复杂度和耦合度似乎踏上了不归路。HW2是我的第一次代码重构,在Poly中用大正则取出各项后创建Term对象保存,进一步取出项中各因子的组成要素后再存入对应Type进行管理,最后Poly-Term-Type调用求导。从类中的方法可以看出,Poly与Term扮演了过分多的角色,尤其是后者几乎把本该属于Poly的化简功能、因子类的解析功能也都包办了,非常难以管理。
HW2虽然混乱不堪,但还算实现了预期目标,即求导+化简,而HW3终于让我吃到了糟糕的架构设计带来的苦头。如果沿用HW2的思路,可基本实现表达式树的构建,经过上一次强测和互测的bug修复,求导的正确性也不会有问题,但是难以进行优化。沿用之后,为了实现递归调用求导,因子类只能选择链表来管理嵌套表达式,这样一来,先前通过比对因子的“全名”进行输入输出化简的方法难以实现,除非冒着TLE的风险,先把链表遍历化简一遍。
这时候,其实应该立即重构,把一切的起点,项的读入,彻底地换个血,一上来就将其化简,再交给Term,但是我却不想因此改变已经没有疏漏的求导方法,最终决定不再重构,放弃了性能分。
二、 bug analysis
HW1:输出时把求导前的系数当成求导后的系数,导致甚至没进入互测。
HW2:获取因子过程指数为0时漏取、求导后因子系数为1时化简错误、化简输出时多输出*号。
HW3:多冗余括号时的TLE。
bug虽多,但是最能说明问题的来自HW2。其余的bug尚可归因于偶然性以及不属于本课程重点的算法问题,但HW2的bug有很大的必然性。通过分析其bug所处的类方法可以看出,它们的产生根源于Term类过于臃肿,功能太多,难以管理。“集大成者”Term几乎与除读入外的所有工作都有关系,处理的数据之复杂可想而知。把多个包含大量特判的方法集于一身,不出bug才令人意外。
三、hack strategy
1.理解程序。
2.按照程序实现流程定位各部分的核心代码。
3.手动构造数据测试各部分边界情况,发现bug时尝试修复,再重复进行步骤3直到完成所有部分。
4.借助自动机生成数据进行多组测试。
四、对重构的思考
在后两次作业中,更好的思路应该是将创建项、创建因子、求导等行为抽象出来,引入更多的类。采用抽象工厂模式,为这些类提供不同的工厂,从而实现解耦,分而治之,如此程序才更具有层次感,方便管理和扩展。
五、对比和心得体会
善用一些设计模式才能够为程序带来层次感和组织感,实现易维护、易扩展、易复用、灵活性好的特点。
从下一单元起,希望自己能够学会抽象,从更贴近问题本质的角度设计结构;不仅仅追求正确,要大胆尝试不同的设计思路,把学会更好地设计程序作为学习本课程的真正目标。