BUAA_OO_第一单元作业总结
BUAA_OO_第一单元作业总结
OO第一单元作业是依据表达式求导展开的,主要目标是为了熟悉面向对象这一思想,学会一些容器,正则表达式等基本工具,感受各类做各事的设计优点。经过将近一个月的学习,笔者在此处对于个人的作业构建经历和个人收获进行总结。
第一次作业
UML类图
第一次作业为简单多项式导函数的求解。不涉及嵌套等复杂结构,代码结构比较简单。其UML类图如下。
-
架构思路
- 第一次作业中共分了3个类,分别是主函数类,多项式类和项类。
- 主函数类中只有main函数用于读取输入,创建多项式。
- 多项式类中采用TreeMap存储表达式的所有项,并采用正则表达式边提取项,边比较,边合并。
- 项类中采用正则表达式进行因子的提取并进行合并。
本次作业基本能实现各类做各事的要求,但在扩展性上思考不周,项及以下的构造方法写的十分面向过程,在读入数据的过程中也过度依赖正则表达式,没有做任何的预处理,因此后续作业中被迫进行重构。
结构分析
- 方法复杂度分析
从复杂度分析数据可以看到,各个方法的圈复杂度基本在合理范围之内。除了项类中的printOut方法的复杂度较高。经分析,是因为该方法中的分支判断语句分支较多,导致选择结构较复杂。
- 类复杂度分析
本次作业中最底层类即为项类,没有下设因子类,导致项类中的方法比较繁杂,复杂度较高。
- 代码规模分析
第一次作业因为比较简单,所设类也比较少,因此代码量很少,只有170行。
关于测评
本次作业中没有在中测和强测中出现bug。
在互测中TLE了一个点。出错原因为在程序一开始未做预处理使得正则表达式会同时匹配空白字符等,在数据点过长时出现爆栈。
本次作业只进行了多项式的合并与排序,没有考虑性能优化问题,例如第一个项的符号,x**2等,损失了部分性能分。
在互测中找到一个bug。一位同学在化简过程中合并同类项之后若出现0则直接将其删除。在输出的时候未作特判使得输出为空。
第二次作业
UML类图
第二次作业为包含简单幂函数和简单正余弦函数的导函数的求解。在第一次作业的基础添加了表达式因子,三角函数因子的概念。由于第一次架构中为添加Factor因子且数据处理部分采用方法不好,故进行重构。
UML类图如下:
-
数据处理
由于第一次作业中未进行数据预处理导致正则表达式过长进而在匹配中出现爆栈。通过在互测中学习他人代码后,采用了对表达式进行预处理的方式。如替换掉制表符空格,**替换为^,外层表达式括号替换为[]等。使得后续利用正则表达式的匹配更简单。
-
架构思路
新增Factor类,实现了获取因子类型,求导,获取系数,获取指数等方法。常数因子,幂函数因子,三角函数因子,表达式因子都继承于Factor,并根据不同需求重写Factor中的方法。
PolyFac中用ArrayList存储Item,Item中也用ArrayList存储Factor。
-
函数调用
在进行建立对象,求导,打印等操作时,由main函数触发PolyFac的相应方法,PolyFac遍历其中的Item后触发Item中对应方法,Item再触发Factor的对应方法,实现最终功能。
结构分析
- 方法复杂度分析
从复杂度分析数据可以看到,各个方法的圈复杂度基本在合理范围之内。
部分方法的复杂度出现飘红情况。经分析发现,个别因子类的toString函数由于因子类含有系数,在输出时特判情况较多,使得对应方法复杂度较高。
其余飘红方法大都是在方法中进行字符串的预处理或者一些涉及到合并的方法,采取的思路比较面向过程,使得其复杂度较高。
- 类复杂度分析
此次作业中复杂度出现飘红的类都是因为涉及到了字符串预处理或合并函数等一些方法,且笔者没有考虑到更好的实现方法,实属无奈。
- 代码规模分析
可以看到此次作业中代码规格较第一次作业提升了将近两倍。但各个类的代码分布还是比较均衡的,没有特别臃肿的类。同时,也具备了一定的扩展性,使得第三次作业没有再花费大笔时间进行重构。
关于测评
本次作业中没有在中测和互测中出现bug。
在强测中TLE了一个点。出错原因为在PolyFac的toString方法中使用了两次Item的toString方法,递归复杂度出现了指数级的上升,进而导致在表达式和项互相嵌套的表达式中,处理时间过长。
本次作业只进行了项中因子的合并,没有考虑性能优化问题,损失了部分性能分。
在互测中找到一个bug。一位同学应该出现了与我强测TLE的点类似的bug,在处理括号过多的表达式中无法运算出结果,处理时间过长。
第三次作业
UML类图
第三次作业为包含简单幂函数和简单正余弦函数及其嵌套组合函数的导函数的求解,且支持部分格式错误检查。由于在第二次作业中考虑到了后续的架构问题,因此改动并不是很大。
UML类图如下:
-
格式检查说明
格式检查采用层层解析的方法进行
- 在一开始的输入中,首先检查非法字符(不可能出现在表达式中的字符)和空格可能导致的格式错误,以便于后续进行数据处理。
- 在表达式因子中,将处理完的数据利用正则表达式进行格式匹配,主要检查项的格式是否正确。
- 在工厂中,利用正则表达式匹配传入的字符串,同时检查因子格式是否正确
每次检查出新的问题都会丢出一个异常到主函数中打印出错误信息。
至此,格式检查全部完成。
-
数据处理
由于在一开始的输入中就剔除了一部分可能的错误,故此处数据处理与第二次作业几乎完全相同。
-
架构思路
在第二次作业的基础上除了增加了格式检查的类与异常类,还将三角函数因子类拆分为sin类与cos类,在其中添加了因子属性,同时,去除了幂函数类与两个三角函数类中的系数属性。进一步贴近高内聚低耦合的设计理念。
其余设计思路都延续了第二次作业。
结构分析
- 方法复杂度分析
本次作业中复杂度飘红的方法都是因为在方法中进行字符串的预处理或者涉及到合并的方法,采取的思路依旧比较面向过程,使得其复杂度较高。
- 类复杂度分析
本次作业中在第二次作业的基础上又多了两个飘红的类。
经分析,主要是因为StyleCheck中对空格导致错误的特判进行了很多的if判断。MainClass中也增加了一些分支以进行格式检查。
- 代码规模分析
可以看到此次作业中代码规格较第二次作业又有了一定的提升了。有两个类的代码较长,经分析,主要还是由于其中的合并方法写的过于复杂。同时,本次作业没有进行重构,主要代码增加量在于格式检查和合并算法。
关于测评
本次作业没有在中测,强测和互测中出现bug。
本次作业进行了项中因子的合并与项的合并,但在因子类的多种构建方法中,有一种构建方法忘记调用合并方法,导致项的合并不彻底。且没有考虑三角函数因子的合并,提取括号的合并,损失了部分性能分。
在互测中找到四个bug。
一位同学在处理(- 1)这种正确格式的情况下会输出Wrong Format,应该是空格问题没有处理好。
一位同学在处理(sin(x)*sin(x)**50)*sin(x)这种格式下会输出Wrong Format,应该是指数的绝对值大于50这一格式判断问题没有处理好。
另两位同学的bug比较玄幻,应该都是在合并方法上的问题。
发现他人bug所采用的策略
由于本人比较懒,所以主要采用黑盒测试搜寻bug。
利用python搭建了一个简陋的评测机,结合题目中给的正则表达式随机生成一部分长度长于要求长度的测试数据以保证数据强度来进行对比,在本地hack成功之后,通过拆分数据各部分具体定位出代码出现bug的可能原因。
除了随机数据以外,也自行构建了一些随机生成几乎不可能生成的数据(如多层括号嵌套等)用来测试。
心得体会
- 通过本单元的作业,我已经逐渐认识到了面向对象编程相较于面向过程编程的优越性。
- 学习并巩固了一些容器的使用,工厂模式的应用,层次化设计等构建方法。
- 基本可以按照需求对整体架构做一个合理的构建。虽然在方法和类的构建过程中还是没有做到高内聚、低耦合,但也算是小有进步。
- 程序的可拓展性一定要在最初的设计中就进行考虑,重构大法虽好,但经常重构也不是那么香。