OO第一单元总结
第一单元总结:
开始OO的第一单元,勉强完成了作业任务,但缺少对OO思想的实践。希望在做了一单元不好的架构之后,可以在之后的课程中更好的领会OO思想,来日方长。
- 总结构成
- 前三次作业分别 程序设计思路、结合类图,度量对单个类,项目分析,事后回顾
- 总结自己的bug,发现的别人的bug,重构经历,心得体会
一、对前三次作业分析
第一次作业:简单幂函数的多项式求导
1)程序思路
对这次作业我设计了四个类,Mainclass,MiHanShu,MiHanShuContainer,UnitPoly.
| 类名称 | 设计目的 |
|---|---|
| MiHanShu | 将幂函数作为最小项存储,其内实现建立,求导 |
| MiHanShuContainer | 存储表达式中的多个幂函数 |
| UnitPoly | 合并同类项 |
2)类图如下:

3)优缺点分析
优点)
由于此时情境简单,常数和幂函数可以都抽象为一种二元项,处理时只需统一处理即可
缺点)
1)未考虑程序的扩展性,在后两次作业加入新的需求后需要整体重构
2)还是面向过程的思维方法,把合并同类项作为一个函数写在了UnitPoly中。导致这个类本身没有意义,同时内部耦合了太多东西。
事后回顾)
因把假期的预习作业基本堆在了第一周,第一次作业完成的较为匆忙,正则表达式的应用是在完成作业时现学的,大部分时间用在试错。此时未建立OO思想,UnitPoly里耦合了太多东西,而把合并同类项写在MiHanShuContainer中会减少。




此次作业较为简单,可从度量分析中看出toString的复杂度较高,因为用了大量if-else判断输出格式
第二次作业 加入了三角函数,括号嵌套
1)程序设计思路
我花费了大量时间处理括号嵌套,在请教同学后,选择了用栈处理嵌套的方法,即1.不停让最小括号入栈,将原括号中内容替换为$i ,直到表达式里不含括号 2.按加号,乘号,分层次解析表达式,建立表达树 3.不停弹出栈中括号,按2.中方法处理括号,并替换表达树中对应位置
| 类名称 | 设计目的 |
|---|---|
| Dealinput | 对输入进行处理,把表达树中负号处理为-1*,替换具有二义性的符号 |
| MainClass | 实现括号入栈出栈 |
| Poly | 存储最小项:常数/幂函数/三角函数,输出结点 |
| Node | 实现建立结点 |
| PolyContainer | 存储求导后可能单项变多项的结果 |
| ListOfPolyContainer | 存储求导 未求导的结果,自底向上组合 |
| NodeTree | 实现建立表达树,替换表达树中$i,输出整个求导后表达树 |
| LsFreiv | 实现按类型求导,表达树中从下至上组合 |
|
InformationOfPoly |
存储求导后各结点信息,作为key值,hashmap中合并同类项 |
2)类图

3)优缺点分析
优点):
架构太乱没啥可写的..
缺点):
完全不是面向对象思想...对类的意义理解有误 把每个要实现的功能写成了函数,放进了类里,而不是根据题意建立对象类,每个类中完成对该对象的操作。不具有修改性,导致第三次作业需求变化,如sin(x)cos(x)不为固定格式,不做合并同类项后修改了大部分的代码。
Poly, InformationOfPoly , PolyContainer ,ListOfPolyContainer, LsFreiv都不是对象,可作为属性或方法放进Node里,不用单独成类 而应针对 最小项类型(sin cos constant power),运算类型(+ -)作为对象,建立类。在Node中调用
事后回顾)
第二次作业时已听说了递归下降处理的方法,但在网上搜索后自学没有学会,只能在后期亡羊补牢。
4)度量分析





dealWithInput复杂度较高,因用了大量小正则进行替换、保护。Node.createNode 复杂度高,因未把各个类型的项单独建类解耦处理,而放到了一起。

由于上述原因导致Dealinput,LsDreiv为“病态结构”,与其他方法的紧密程度过高,应按类型分开处理求导。
第三次作业 加入了格式判断,三角函数括号内可嵌套表达式因子
1)事后回顾
由于之前对表达式的处理是建立在大量的预处理基础上,所以需要新增格式判断功能。事后回顾,此时应是重构的最好机会,但我怕时间来不及就选择了在第二次作业的基础上进行改动,切身体会了对一份架构不好的代码进行改动是多么费力不便,以及改动后的代码架构甚至更不好了,导致我放弃了化简,性能分全0...
2)程序分析
请教了同学,终于学会了递归下降处理表达式,增加了格式判断的类checkFormat(还是面向过程)。更改了sin cos的存储,增加了嵌套的运算处理。 由于未采用面向对象,这些改动都在内部,更改起来很麻烦,要处理相联系的情况,然而宏观上没有做改动。其余类的设计与上次一样,故不附表格。
i)类图
由于更改后架构更差,自动生成的类图已经没有意义,故自己画了uml图。

ii)优缺点分析
优点)
比上次更乱,没啥可讲的...
缺点)
除了继承第二次作业的所有缺点外,我为了保证正确性,没有思考对括号的输出优化,加上用的表达树方法导致不好化简,最后无脑加括号输出导致结果性能分为0.
3)度量分析




除上次的病态结构外,checkFormat调用了多层下层check,导致复杂度过高。

在总类复杂度上,可以看出我确实没有对上次不好的架构进行任何更改,LsDreiv和DealInput的OCavg 即平均循环复杂度过高,应被分为小块再组合。CheckFormat的WMC 即总循环复杂度过高。
二、总结分析
1.自己出现的bug:
第一次作业简单,未出现bug
第二次作业对表达式的预处理出现错误,在DealInput类中对*后出现的+,-号未进行保护,导致被错误替换出现问题;处理中未涵盖括号嵌套的情况。
通过度量图,发现DealInput OCavg过高,对其他类依赖过大,同时里面有许多小正则匹配、替换,造成代码复杂度高,代码行数大,对比其他未出现问题的代码,这些代码复杂度相对较小,导致在我调试的过程中的测试也比较充分,出现错误的几率更小。
第三次作业单独成类的checkFormat中判断指数大于50用了int类型,导致RE
2.别人出现的bug:
我在第一、二次作业时结合了别人的代码设计了样例,我看别人代码时特别关注了他们识别连续的符号,处理多个不同相组合,以及优化输出的部分。
3.重构经历:
在第一次作业时由于表达式处理的局限性大,化简方式也只能针对仅有幂函数的情况,故在第二次作业时重构。但由于我对面向对象的思想未能理解,所以这次重构仅帮助我通过了中策,而未对学习OO思想,提升架构能力有帮助,故此处不做分析。
在第四周时,趁没有编程作业我用递归下降的方法重构了第三次作业。
重构前的第三次作业类图

重构后类图:

重构后采用面向对象的方式,建立抽象类Item,建立运算方式Add Mul,因子类型Constant,Sin,Cos,Power,Expression,处理表达式成分时递归调用。
4.心得体会:
1)关于未能实现OO思想的反思:
这应是本门课程的主要教学目的,但我并未在第一单元将它实践应用,面向过程的编码方式导致我在调试自己程序的时候有诸多不便之处,常为了修改部分代码修改牵连的大量代码。
通过课上的学习,与在互测中学习别人的代码,我体悟到了OO思想的好处,一个良好架构的重要性,即在可读性,修改的少量性,可拓展性上的优势。在第四周我用递归下降的方式,OO思想重构了第三次作业,亲身体会到了一个好架构对程序的便利,重要之处。我会在接下来的课程中实践这一思想。这段失败的经历也让我体悟到光实现作业要求是不够的,实现多项式求导不是目的,而是要在实现这个需求的过程中进行架构设计,实践OO思想。
对于第一单元的反思:提前把前置性的、基础性的东西弄好。由于把预习作业拖到了开学第一周,以及花费了大量精力处理括号嵌套,复习递归、数据结构基础等知识,在第二次作业完成后由于时间紧迫没有选择重构,第三次直接在上面添加了检查格式的接口。
2)关于处理问题的思想
化整为零的思想,把复杂的东西按层次拆分,直到处理成简单的,可用单模块处理的样子。这种层层下降,直到底层进行真正处理的方法,可用递归的方式处理。
3)写代码过程中的收获
i)有意识用java内置的方法,而不是自己造轮子。
在这三次作业中,经常有自己写了一段实现功能的代码,结果发现java已内置该方法的情况。在有意识的写代码前,先搜搜java相关方法时,也出现了新的问题,即盲目用内置方法会出现问题,如直接arraylist.remove会报错,此时可通过阅读源码解决。
ii)自己全面测试的重要性
在第二次作业中我过了中测就没再自己测数据,结果在互测中被hack22次,还都是很容易被构造出的数据,希望以后吸取教训,自己在提交前进行全面测试,按提议构造边缘数据,易出错状况。
浙公网安备 33010602011771号