面向对象设计与构造第一单元总结

面向对象设计与构造第一单元总结

​ 2021年3月初到三月末本学习面向对象课程第一单元已经结束,本单元的核心为表达式求导,分为三次作业进行,作业难度和数据复杂度呈迭代增强的关系,因为初次接触面向对象所以需要改正的地方很多很多,因为每一次构造都有局限性,所以重构了三次(呜呜~),不足之处敬请大家批评指正,感谢各位朋友啦。

一、三次作业程序分析:

·第一次作业:

内容:实现简单幂函数的多项式求导,包含连乘形式的幂函数。

设计思路

​ 本次作业的需求较为简单,所以在主类读入的基础上建立了三个类:DealExp用来处理读入的字符串,将字符串的格式进行优化,处理不必要的制表符\t和空格,然后对由于项和因子的复合造成的符号连接进行化简,方便之后的求导分析,然后通过处理字符串进行拆分项;用正则来对读入的项进行分类,SingleX类用来处理单个x的项或者常数,MultX类用来处理多个x连乘的项。最后将求导后的结果以指数为key,系数为value放在一个HashMap当中进行合并同类项,最后输出。

程序模块设计:由图中可以发现在处理模块设计复杂度方面还有很大的问题,导致模块内部出现了高耦合的问题,而且在处理幂函数的时候结构复杂读略高,使得代码难以维护。

image

程序类数据统计
image

程序UML类图

image

优化思路

​ 优化主要就是合并同类项,并且将第一项输出挑选一个正项,减少一个+的长度,但是还是不够优化,在hack同学的时候观察代码时发现同学处理了x**2x*x

BUG分析

​ 自己在强测中没有bug,但是在互测中被发现了一个BUG,被同房间的同学反复HACK了10次,主要错误是处理因子和项复合之后的符号问题,先处理了两个符号,后处理了三个符号,这会导致将三个符号的处理之后还会留下两个符号的问题,修改BUG的时候只需要将其调换位置即可,其实这样的BUG不影响评测的正确度,虽然可能被无辜hack了,但是也提醒了我要注重逻辑结构,多多思考。

·第二次作业:

内容:在原先表达式求导的基础上增加了sin和cos函数但是其括号内只能为x,另外在整个式子中增加了括号的嵌套。

设计思路:

​ 相比较于第一次作业,这一次的作业进一步使用了面向对象的方法,在第一次作业DealExp的基础之下先行拆项,利用+-的连接,将表达式初步划分,然后将有括号的项送入BracketItem类中,利用三个栈进行拆括号处理,发现表达式中所有的项都可以划分为a*x**b*sin(x)**c*cos(x)**d的形式,这样的化整为一的确大大降低了本次作业的实现难度,但是也缺乏扩展性。对于当前任务需求的过度抽象整合可能会降低可拓展性。然后将拆得的项送入一个HashMap,这里使用了对HashMap中的key进行重写,这样就能建立一个以x、sin、cos三个项的指数为key的map进行合并同类项了,然后再对其进行求导,输出结果还是放在同上述重写过key的HashMap中,这样就可以继续合并求导后的同类项啦。

程序模块设计:这次的模块设计问题更加显著,主要是因为在处理递归过程中耦合度极高,在三个栈之间进行pop和push操作使得模块之间调用关系密切,看来代码质量还需要提升的空间很大很大。

image

程序类数据统计:

image

程序UML类图

image

优化思路

​ 优化思路同第一次作业,包括常数0的删除,常数1的省略,合并同类项等,这里还可以进行sin和cos的三角函数公式化简,但是由于在递归方面花的心思比较多,没有来得及对三角函数好好琢磨,算是很可惜了。

BUG分析

​ 这次在强测中有一个小BUG就是在处理括号的BracketItem类中,有与递归的过程比较复杂,导致合并同类项的时候疏忽了乘积项最后的合并方式,在乘积之后入栈需要对map进行查找,合并同类项,但是由于忘记合并了,出现了缺项错误,导致强测因为一个bug扣了不少分。

·第三次作业:

内容:在的第二次作业的基础上增加了三角函数括号内的嵌套因子,同时还增加了格式判断的问题,这次作业的难度大大增加,尤其是在递归处理括号的部分花费了大量的时间,由于前几次的构造都没有很好的拓展性(太菜了),这次又不得不重构。

设计思路:

​ 本次作业难度较大,重构是需要对每一种因子进行设计这里我单独分出了Constant、Cos、Sin、Power等因子类,在其之上设计一个接口Poly负责求导,将数据于其顶层行为分开。由于这次作业增加了格式判断部分,所以整体输入输出格式还是继承了第二次作业,首先对输入的字符串进行格式整体判断,使用以下几个正则对如空格和制表符的错误位置进行判断

String space = "[ \\t]+";
        String signNum = "\\*[ \\t]*[\\+-]" + space + "[0-9]";
        String multsign = "[\\+-][ \\t]*[\\+-][ \\t]*[\\+-][ \\t]+[0-9]";
        String num = "[0-9a-z]" + space + "[0-9a-z]";
        String index = "\\*" + space + "\\*";
        String triNum = "(sin|cos)[ \\t]?\\([ \\t]?[\\+-][ \\t]"
                + "[0-9]+" + "[ \\t]?\\)";

然后将空白符以及制表符进行消去处理,初步化简输入表达式,如有初步符号错误则直接输出WRONG FORMAT!,否则进入递归求导环节。在这里还是使用了正则结合循环进行处理,首先开了两个栈,分别是因子栈和操作符栈,然后采用自上而下递归求导,将识别到的因子送至对应的因子类当中,将识别操作符作为判别依据对栈中因子进行处理。同时在递归的过程中判断字符组合错误格式括号匹配错误格式等,在输出求导部分采用左右子树形式,化简处理过程,由于递归难度较大,笔者对于内外括号的递归方法不是很熟练,所以采用了左右子树处理,这样一来能够稍微化简递归难度,但是在性能方便大大削弱,因为这种方法会产生多余的括号。在因子类中重写了ToString方法用来输出求导结果。最后在输出过程中利用正则进行部分优化。

程序模块设计:第三次作业相较于第二次在结构上有了很大的改观,主要是在递归处理上查询了不少资料看了很多Csdn、博客园、Github上的帖子,在处理因子求导的时候有意识地将其进行了接口分类,但是在递归模块中复杂度依旧很高,由于两个栈的操作频繁,导致函数内部地循环判断代码量极大,函数跳转也多,代码质量还是不高,

image

程序类数据统计:

image

程序UML类图

image

优化思路:

本次作业几乎没有什么高效的优化措施,因为这次递归难度较大,查阅了很多资料和方法,最后由于时间和精力选择了最暴力、最原始的递归办法,导致输出的结果非常的冗长,因为这种办法无脑取出因子加括号再与算术运算符连接,所以在最后输出优化的时候也费了很多心思,但是效果一般,主要就是将特殊的加减复合符号、常数0、常数1的特殊位置处理化简,而且还去除了一些非必要的括号。

BUG分析

​ 这次强测中也有一个小BUG,就是在优化的过程中对于0和1的处理考虑不够全面,导致出现了()*+()这种形式,最后通过对正则的修改完善,也是解决了这种省略化简得小错误,同样在互测中也因为这个错误被同学反复hack了13次。

二、互测方法:

​ 1、首先依据自己在设计完成作业过程中遇到的坑和阅读指导书时发现的一些极端例子进行记录,将这些数据保留下来,等待测试,同时在水群中学习了解一些大佬的共享测试数据进行测试。

​ 2、由于笔者个人不赞同无脑的黑盒测试,所以采取了自己构造数据的办法,(因为看到同宿舍同学,一天二十四小时不停歇跑评测机,hack同房一百五十多次·······也不管是不是同质bug,个人认为这样子不利于学习同学的代码和发现bug,只是无脑刷分,而且没意义)。

​ 3、笔者和同学使用python写了一个便捷测评小程序,就是将同房间的所有代码放在一个文件夹内,然后在python中输入自己构造的测试数据,通过调取每一个文件的Main类进行测评,这样就实现了一次输入测评多个java代码的效果了,结果评判方式采用的时python自带的sympy库,这样可以简单又便捷,不用再一个一个打开IDEA运行啦。

(这里有一点小小的漏洞就是,sympy不支持含有前导0的数字,但是一般情况下前导零大家都用正则处理字符串转化为Biginteger的时候处理掉了,所以不会担心大家有前导零的错误,我的测试数据就可以不考虑前导0啦,但是会考虑单个0~~)

附上效果图:

image

三、经验收获:

  • 进一步加深了递归的能力
  • 有意识地了解使用面向对象编程思维
  • 能够有一定的模式设计思维
  • 在实验课程中对继承、接口、工厂模式有了更加深刻的认识
  • 提升了注重细节的思维(多次因为细节问题出现bug)
  • 阅读了同学的代码,学习了一些优点
  • 大幅度改观了代码风格,已经不怎么依赖checkstyle插件了
  • 增强了debug的能力,对bug的分类更加清晰

四、总结与感悟:

​ 虽然三次作业都进行了重构,但是由于第一次作业形成的流程框架没有变,所以处理的步骤都是一样的:先读入表达式->对表达式进行初步化简->进行拆项、提出因子(第一次直接拆,后两次递归)->对因子进行求导->对求导后的结果进行合并化简->输出。

​ 比较可惜的就是第一次前两次还能使用HashMap进行同类项的合并化简,第三次由于递归难度太大,不得已放弃了最基本的化简方式,但是在三次作业中也渐渐能够采取面向对象的办法简化问题,渐渐采取了对因子分类的形式,类似于工厂模式处理。在处理的部分由于考虑到大正则的局限性,都是使用了拆项加多个小正则进行提取表达式。因为自己可能之前在递归的练习方面不够,导致本单元的作业花费了很多时间,感觉自己有一点点偏离主题,后两次作业在对象化的构造上只花费了小部分时间,而大把大把的时间用在了递归的处理,可能还是因为相关的处理技巧不够熟练。

​ 总之三次作业也使我渐渐有意识地使用面向对象的设计模式,代码能力也有提升,还希望自己能够继续努力,认真完成OO作业,在每一次的任务中锻炼自己的能力和面向对象思维,最后非常感谢学期初给我们帮助的老师们和助教学长,也感谢多次交流心得经验的同学们。希望自己能砥砺前行吧。

posted @ 2021-03-25 22:34  Aaron-Huang  阅读(109)  评论(0编辑  收藏  举报