BUAA-OO 第一次博客作业--关于Java实现表达式求导的思考

前言

在前三次OO作业中,我们重点对程序的输入输出设计、程序结构与层次化设计、程序鲁棒性进行了学习与应用,实现了由易到难的表达式求导程序。对于之前一直习惯于面向过程编程的我而言,这三次作业着实是一个不小的挑战。由于对于程序结构性设计的考虑欠妥,导致每次作业的拓展性较差,越往后走越能发现程序出现bug的机率上升,而且bug的查找与修复过于依赖测试点,修复过程中也存在按下葫芦浮起瓢的情况。借助这一次博客作业,我将仔细分析前三次作业代码中的结构问题,依据在研讨课、讨论区等学习到的经验进行总结。


 

程序结构性分析    (运用IDEA自带的Metrics和Diagrams插件)

第一次作业结构性分析:

第一次作业结构设计如图:

 

 

 

 

 

 

 

 

 

 

可以看到,由于第一次作业相对来说较为简单,当时对于面向对象编程也不了解,第一次作业的代码是很“C-style”的,只建立了一个多项式类,在多项式类里完成了所有的运算、输入输出部分,把对象里的方法当作了一个个函数,显而易见,这样的设计结构对于程序的可拓展性是灾难一般的打击。通过在main方法里进行整行读入,在split方法中通过正则表达式匹配每一项,对每一项分别求导后合并输出。对于这样的思路,我认为对于被正则表达式匹配的每一项建立一个Term类是一个更合适的方法,在term类中进行求导,判断合法性、化简等相关操作。这样既能提升程序的可拓展性,也能降低程序出现bug的机率,简化程序复杂度。

 

第二次作业结构性分析:

第二次作业中考虑到了由于第一次作业结构的不合理导致的许多问题,将程序分为三个类,在Main中进行输入输出处理,输入的表达式在Poly中进行拆分,对拆分的每一项新建PolyItem类,在PolyItem类中进行求导,结果返回到Poly中,并在Poly中进行表达式的化简,最后输出。

可以看到由于Poly类由于使用大正则匹配每一项从而将表达式分离,得到的结果继续在Poly类中合并、化简、输出,使类中方法过多,从而导致Poly类的复杂度过高。

第三次作业结构性分析:

第三次作业依然根据正负号对表达式进行了拆分,但是在求导过程中先根据每一项进行分类,先判断最外层的函数类型,求导后递归得对内层进行判断类型,直到内层可以被第二次作业的正则表达式匹配或被判为违法形式。

可以看到第三次作业中有四个类的复杂度都被判为过高,这是因为程序的结构性设计过于简单,导致每个类的功能过于复杂,再听取了研讨课发言的同学以及讨论区老师的意见之后,本次作业应该对每个项的类型建一个类,在读入的时候对每个表达式进行拆分新建对象,并建立求导接口使每个类(即每种类型的项)都具有求导功能,从而清晰化本次作业的结构设计。


 

 

分析自己程序的bug

在第一次作业中,主要是由于对于正则表达式的理解不够到位

由于空格在正则表达式中被写成了‘ ’,会导致正则表达式匹配引号

并且对于非法输入的考虑不够全面,在最终输出的时候只是简单的表达式全部被抵消的情况输出0,而没有对特殊情况进行特判,导致 1+ 这类非法输入输出0

 

第二次作业

强测与互测均未发现bug

 

第三次作业

由于设计的不完善,导致在写代码过程中就发现了很多bug,并且bug的修复过多的依赖于对于表达式的非法输入进行特判,而不是靠程序整体的层次化设计,这也直接反应到了测试结果方面

在强测与互测被发现的bug中,有很多不同类型的bug,在很多地方都出现了判断不完全的情况,这一方面是因为对指导书的理解不够深入,更是因为程序的层次结构设计不好,各个类中的表达式形式判断划分不够清晰,每个方法的功能过于复杂。例如在互测过程中被发现的bug sin((-(x))),被我判定成了非法输入,就是因为程序的递归终止条件为能够被第二次作业中的求导函数直接求导,但-(x)使无法被通过的,也无法继续化简,从这也可以过于依赖特判的缺陷之处。

除此也可以看到,由于对于结果优化的不充分,导致性能分被扣掉很多,这也是需要在后面的作业中需要加强的一个点

就拿我的作业来说,第二次作业中由于只是记录了sin(x),cos(x)以及x的幂以及系数,并且只对sin(x)^2+cos(x)^2=1尝试优化,就发现在优化的过程中经常会出现负优化的现象,在第三次作业中由于设计的混乱,每次递归得到的均是已经求导完成的字符串,无法对多余的括号嵌套进行简化,更无法对三角函数的可优化性加以利用,这也是我对我的第三次作业相当不满意的一个点。


 

发现他人bug

每次在编程过程中将自己发现自己程序的bug记录下来,再加上我认为可能错的人比较多的地方与Java随机生成相结合

javac编译所有人的程序,对所有人的程序跑相同的测试用例,利用xeger通过正则表达式生成测试用例,运用matlab的自动化简功能或者sympy比较所有人的输出结果

总体上是自己构造为主,自动生成为辅的方法

在大量构造测试样例之外,也会读他人代码的主要部分(例如前两次作业部分同学写的大正则),从而更有针对性的发现bug

不知道以后的作业是否还能应用随机生成大量测试用例的用法,但是个人认为还是应该多读其他同学的代码,设计好的地方多向他人学习,不好的地方也能吸收经验教训,读完代码也可以更有针对性的发现别人的bug,也希望打家能够在readme中表述清楚自己的设计思路,或者在代码中以注释的形式加以说明

在前两次作业中,求导的逻辑较为简单,构造的测试用例主要以非法格式为主,而在第三次作业中由于求导逻辑的难度加大、以及嵌套因子和多项式因子的引入,大家的程序中都会或多或少的计算性错误,从课程组对于互测的要求也可以看出这一点


 

Applying Creational Pattern

 在学习了Java几种常见的测试模式之后,我发现自己的编程设计思路基本上为简单工厂模式,前三次作业都没有用到接口和抽象类,使得代码看起来不够简洁清晰,层次显得比较混乱,很多方法和类过于冗长复杂,尤其第三次作业以能够被第二次作业中的求导方法识别为递归终止条件,显得代码很有C语言的味道,这是没有达到课程训练要求的。在以后的学习中要好好领悟更多设计模式的思想。


 

总结

经过这几周对于面向对象课程的学习,从对Java语言一窍不通到现在已经能够运用其编写数百行的程序,这是一个进步,但绝不能满足于此,从作业中可以看出对于面向对象思想的理解,对于Java语言特性的把握不足,对于设计模式的生疏都是在今后的学习中亟需加强的部分。在今后的作业中要更多的运用面向对象的思维方式,更多的运用接口、继承等方法,使自己的程序能够更多地具有面向对象的味道。

posted @ 2019-03-26 15:56  wttth  阅读(243)  评论(0编辑  收藏  举报