面向对象第一单元总结——表达式求导问题
前言:现在开学已经快一个月了,四周的时间也匆匆过去,面向对象课程第一单元已经结束了,或多或少也算有些收获吧,在这里总结一下自己第一单元的收获与感想。希望每隔一段时间就回过头来看一下走过的路,虽然肯定不会尽如人意,但是可以让我知道哪里做的不好,后面再做时能够更加用心一些,更加认真一些,人不就是这样慢慢成长和进步的吗。
本单元的面向对象作业是多项式求导的迭代式开发,从第一周的单个幂函数求导,到第二周含有乘法项的求导,再到第三周增加了嵌套函数求导规则,难度是逐渐增加的,在思考如何解决问题的过程中我也慢慢体会到了到底什么是面向对象进行程序设计,学到了怎么用基本单位类来管理对象的属性和行为。下面在对三次作业分析的过程中穿插着总结一下自己的收获与感想。
(1)基于度量来分析三次作业及bug;
第一次作业
第一次作业就是简单的幂函数多项式的求导问题,这里我只建立了一个cell类来管理每个幂函数项的属性和求导行为,在Main函数内部根据每项的指数进行合并。
由于所有的属性和行为都放到了Cell一个类内部,所以显得Cell类就特别庞大臃肿,特别是对每个幂函数项的解析工作也交给了Cell类内部,这样导致了处理输入和cell类的行为耦合在一起,不符合高内聚、低耦合的原则。
现在回过头来想的话,可以将程序的各个环节分给不同的类管理,比如输入类,解析类,求导类,合并类,输出类。这样就能大大降低一个类内部的复杂度,也易于管理。
[^]: 第一次作业复杂度分析
从复杂度的分析也可以看出,基本所有的函数都来自Cell类,其中CalTerm函数的复杂度最高,因为他是用来解析每个幂函数项的,更好的方法应该是,解析单独建一个类,然后只把解析出的系数和指数传递给Cell。
第一次作业bug分析:
第一次作业的其实就一个显而易见的bug,就是在解析的过程中忘记了\t,这个bug其实我在提交之前就想到了,但是一直拖到最后也没动手改,到最后导致强测、互测出现了很多问题。在bug修复阶段只需改了一行就决解了强侧、互测的所有测试点。
这件事说明:问题不解决它永远都是问题,你不自己找问题,问题就会来找你。
第二次作业:
第二次作业与第一次作业相比,难点主要在于:
- 如何判断输入的表达式是否符合表达式的规则
- 如何解析出每一项,以及每一项的每一个因子
- 如何对每一项求导
- 求导后,如何进行项内合并,如何合并多个项
首先对于表达式的判断,我在第一次的基础上采取了分层式正则的解析方法,构造了一个大的正则表达式,不符合该正则的表达式被视为WF,同时也用该正则识别出每一项,以及每一个因子。
由于前期考虑不周,还是按照第一次作业的思路,在对每个项求导时,对每个因子求导再拼接其他因子,这导致几乎无法在项内合并化简,项与项之间的合并化简就更不可能了。所以这次作业在保证准确性的前提习下我没有进行化简,所以强测的每个点几乎都是没有性能分。
我在互测的过程中学习到了另一种好的架构,就是把每一项看成一个对象,利用四元组(系数,x指数,sin指数,cos指数)来管理数据,这样就将求导、拼接输出等都变成了统一的形式,项内的化简,项与项之间的合并也变得轻而易举。
所以磨刀不误砍材工,在想清楚整个程序的架构之后再动手比盲目动手要高效很多,这在我做第三次作业时体会很深刻。
结合上述UML和复杂度分析可以看到,本次作业相较第一次,变得复杂很多,甚至比第三次还要复杂,由于沿袭了第一次作业的架构,第二次作业分为三角函数类和幂函数类,相当于第一次作业的迭代。第一次作业结构中不清晰、的毛病在第二次作业迭代后显的尤为突出。
第二次作业bug分析:
由于本次作业采取了很保守的求导方式,所以在强测、互测阶段没有出现bug。但也因此导致输出的结果异常复杂,几乎没有化简。
第三次作业:
第三次作业的难点在于引入了嵌套的规则,吸取了第二次匆忙动手的教训,在第三次作业动手前,我花了较多的时间构思整个程序的流程。主要包括:
- 如何处理输入,判断WF
- 如何构建子类
- 如何进行递归式求导
我采用了课上老师提供的一个思路,就是将求导作为一个接口,简单函数(幂函数,常数,简单三角函数)、加法组合、乘法组合、嵌套组合、表达式因子都作为求导的实现,同时配合简单工厂模式根据字符串返回相应的对象类型(见UML)。可以实现递归式求导。
由UML和复杂度分析可以看到,本次作业调整了整体流程以及对不同的因子和组合规则分类之后,复杂度不是太高,甚至低于第二次的复杂度。
第三次作业bug分析:
本次作业从周二晚上构思一直到周四下午才开始动手,写代码只用了不到一天的时间,相较于第二次(写了三天)有很大的进步,还是在于提前分析好了架构,动手时才能把所有方面都照顾到,不必手忙脚乱。
但是在提交中测过了之后,我便没有再管,其实这是非常愚蠢的,我没有意识到测试是工程中很重要的一个环节,而是觉得写完了,就万事大吉,自动评测机的实现也是一推再推,没有实现。
所以我也为这个愚蠢的行为付出了相应的代价。就是强测只过了了了几个点,甚至都没能进互测。可能也因自己一天就写完了这个复杂的程序有些骄傲吧,蒙蔽了双眼,没想过我这样不测试就交到强测上的后果。结果也是铩羽而归。
bug也有很多,主要有以下几点吧:
1、对因子多次简化,导致内部变量累计变化,造成输出错误
2、WF情况判断不全,该输出WF却不输出
3、表达式解析有问题,复杂一点的情况就不能解析。
……
本想着第三次互测要好好表现,争取多得分,也能学习学习别人优秀的架构。结果却不尽人意。希望自己吸取教训,以后能够丢掉那些狭隘的、自满的情绪,认真做好一件事吧。
(2)分析自己发现别人程序bug所采用的策略;
分析自己的bug主要还是通过样例没有通过然后才去程序中找是哪的问题。策略就是要先思考一下可能是哪里的问题,然后再去找。不要盲目地从main开始调试,效率会很低。
第一次互测我用python生成了很多随机表达式,然后默认我自己的输出结果是正确的,对我们屋里的成员hack,效果还是蛮不错的。第二次有些疲软,重点放在了学习别人代码上,没怎么进行hack。第三次本想着要大展身手,却因眼高手低连互测都没进。
(3)应用对象创建模式来重构;
由于每次作业都没由考虑架构的可扩展性,所以虽说是迭代式开发,但是每次作业我都进行了重构,这也是我觉得比较遗憾的一点,没有用心体会面向对象设计中很重要的可扩展性的要求,每次作业都是匆匆做完了事,没有进行深入的思考。
不过我还是有所收获的,主要是第三次作业,首先我意识到这次嵌套规则的引入使得不能简单地构建一个类然后求导,要有清晰的类、类与类之间关系的架构用于递归求导。所以采用了求导的接口,然后因子和组合规则将其实现。这样每次调用一个式子的求导函数,就能层层递归,直到返回一个简单因子的求导结果。其中对于不同因子所要创建不同对象的问题我采用了简单工厂模式来解决。
(4)对比和心得体会
也可能是因为在家不能回学校的原因吧,我觉得自己太懒散了,四个星期来没有认真体会这门课程,作业也都是匆匆做完了事。虽然完成了既定的任务,但还是觉得自己学到的东西不多。比如HashMap,TreeMap,工厂模式,函数复写。这些东西也都是一知半解,做题时还是用老一套的ArrayList,甚至不想去探索。我知道只有主动才能学到更多,只是需要克服那一层窗户纸的恐惧,知道学会一点东西其实没有那么难,学习应该是一个快乐充实的过程,而不是恐惧和痛苦。所以希望自己能再主动一些,再勇敢一些。
另外,在整个面向对象课程的设计中,我应该更多的了解和学习现在工业界行业的标准是怎样的,比如了解业界软件开发流程。包括软件的测试常用的方法。与实际相结合才重要的。
世界还等着我去征服呢,旅途中的这点困难又算得了什么!加油吧勇士!