面向对象第一单元总结
面向对象第一单元总结
第一单元的三次作业需要完成的任务分别为:多项式求导,简单三角函数和括号嵌套,三角函数内嵌套括号及格式检查,下面将以第三次的最终功能完善版代码来进行总结和分析。
一、基于度量分析程序结构
设计思路
- 首先在第一次作业中由于对于正则表达式运用的不熟练运用和设计的冗杂、不清晰导致作业失败,于是在此之后决定采用对字符串的预处理来进行之后的工作。
- 在整个代码的实现中,总体上分为了三步,分别是格式检查与空白符消除,符号化简,求导处理,通过这三步来完成了整个任务。
- 格式检查
- 对空白符格式的检查
CheckSpace完成了对表达式中不正确位置的空白符的检测。 - 消除空白符
DeleteZero完成了对表达式中所有空白符的清除。 - 内容格式检查
CheckFormat对无空格的表示进行内容上的格式检查,其中用到了NextTag类,用来反馈每一个字符的后面应该或者能够存在的所有字符类型。把NextTag的结果和后面的字符进行比较就能快速获得检查结果。 - 格式错误异常类
WrongFormat继承自Exception,当前面检测到错误格式时,立即抛出该异常。
- 对空白符格式的检查
- 符号化简
- 这里没有新建一个类来完成这个操作,而是直接在
Main中用正则表达式完成这个任务。
- 这里没有新建一个类来完成这个操作,而是直接在
- 求导处理
- 求导入口
Deprive,通过此类中的方法能对其中的表达式进行求导。求导操作首先要对表达式进行一个初步的合并同类项,然后以+-为分隔符,把其中的项先一个一个取出来,并用TypeCheck判断这个项是否是常数或者一些特殊的单独因子类型,再把项的表达式和它的类型共同用Term保存起来。同时新建一个容器来存放每一个新的项,之后再遍历这个容器对每一项进行求导操作。 - 对项求导
CF通过乘号*来解析出项里面的每一个因子,然后再通过复用TypeCheck对每一个因子的类型进行判断,并使用对应类型的求导类对其进行求导。求导类一共有变量因子BLyinzi,常量因子CLyinzi,三角因子SJyinzi,嵌套因子QT四种求导类型。而缺少的表达式因子类则直接递归调用求导入口,把它视作一个全新的表达式进行求导。
- 求导入口
- 格式检查
第三次作业复杂度分析
类图

代码总体分析

- 代码总体上非常冗长,逻辑不清晰。所有代码都有过长的问题,这既不方便阅读也不便于后期修复BUG。
- 代码工程的创建还是没有工程的意识,所有代码的书写都还是在为了完成任务,没有写注释的习惯;代码间的空行也较少,各个部分之间不够清晰。
- 代码风格的问题非常突出,例如在命名上可见一斑。
类复杂度


方法复杂度

- 目前在作业中的解题思路还是有较重的面向过程的痕迹。在做作业的过程当中很多时候为了省去对新类的构造和管理,选择了直接在一个方法里面实现许多同等级的核心功能。这也造成了代码之间功能的耦合程度很高,时常为了修复BUG导致牵一发而动全身。
- 使用了大量的分支控制及其嵌套,代码逻辑上非常繁杂而不直观。
- 存在一些大段代码重复的问题。
- 代码的可迭代性并不强,在架构上过于粗暴直接,缺乏全局考量。
- 没有考虑到要使用接口,覆写等面向对象的标志性的方法去解决问题,需要优先考虑如何用这些特点去解决问题。
二、分析程序BUG
- 在判断
WRONG FORMAT!的问题中,采用了和求导处理中同样的字符串预处理模式。但由于自身层次设计过于抽象的问题,导致始终无法提取和周全考虑到每个字符所处位置的特点,导致在这上面的判断不仅非常繁杂同时引入过多中间变量,同时还有很多隐性的BUG可能即使在目前的BUG修复之后依然存在。 - 在进行求导操作时,对于前面带负号的项的求导处理不够干净。总体上处理思路是先把负号单独去掉,对后面进行操作之后再把符号加回给得到的导函数表达式。但问题在于一些函数——例如幂函数、三角函数——求导之后幂次等要素的提前使得表达式意义发生改变或是输出格式变得不合法。一开始由于这方面分析比较复杂,没有考虑周到,少了很多括号。
- 在求导后的输出上,括号问题表现得尤为突出。一开始的考虑是尽可能地去精简括号的数量,但是在实际操作之后发现很多情况复杂不好区分,结果表达式意义越来越乱。直到最后为了保证正确性,只能选择无脑给绝大多数项添加括号。
三、分析互测策略
在互测阶段,我总体上采取了三种比较经典的互测策略。
- 多种要求组合测试。将作业中的各种要求——尤其是
WRONG FORMAT!的部分——充分混合起来,考察程序设计耦合程度是否过高导致灵活性不够,或者是由于程序特性的不熟悉导致的隐性BUG。 - 边界长度数据测试。尽量用各种类型的边界长度的数据去检测是否程序有不合理的过多嵌套导致的爆栈问题,要求程序设计者尽可能通过对问题的全面思考,减少对计算机性能的依赖,同时也提高自己的程序运行性能。
- 全面考虑,寻找作业要求的盲区。仔细认真地阅读作业要求这个要求恐怕很多同学在初次构建作业代码时都没有能够做到,等到评测机要测试了才开始重新认真看要求,找BUG,这也是多数同学用自己的逻辑测自己写出来的代码没有BUG,但做不到AK评测机而痛苦的原因。通过摆脱作业要求书面上的空间局部性,全盘考虑整个要求,总能发现一些第一次没发现的格式组合。同时实践证明,因为这个问题被Hack的同学占比不小。
四、重构经历总结
- 在第一次作业之后,受制于我本就捉襟见肘的正则表达式技巧,我选择了用表达式预处理的方式去完成后面更多因子的加入和三角函数的嵌套。费尽心思、绞尽脑汁尽可能地去考虑每一个字符所可能代表的意思,以及它所能出现的位置。但直到现在,这个问题也并没有能够得到很好地解决,基本上还是处于自身思维范围之内,应当也还只是相当有限的判断。
- 由于第一次作业的难度并不算大,所以我大量使用了面向过程的架构和解题思路在解决问题,还是在
main函数中单打独斗。在第二次作业中,开始重新思考如何使用不同的类,实现不同的功能,再把它们组合起来解决而问题。这个过程大致类似于考虑把面向过程中的函数转换为一个一个的方法,参数转换为一个一个的类。
五、心得体会
- 在重构表达式分析功能的这个过程中逐渐体会到了对表达式本身不分层次、阶段的分析效率是极其低下的,而如果采用面向对象的办法,去关注同一字符串不同部分、不同层次的内容则多数情况能找到一些新的解决办法。
- 解决问题之前对问题本身深思熟虑的分析的重要性不亚于在代码实现中对架构实现的考虑。甚至来说,根据亲身经历,我认为前者至少应当占到时间和精力占比的40%。分析清楚整个问题到底有哪些问题,各个部分内部的细枝末节又需要怎么实现,之后其实架构本身就已经快要浮出水面了,后面的代码写起来也是水到渠成,同时又能兼顾细节,事半功倍!

浙公网安备 33010602011771号