第一单元的求导作业让我在面向对象的过程中逐步深入,下面我来分享一下我的架构过程与收获。
第一次作业:
架构相对简单,包含了三个类:
一个单项类,存储指数与系数,含10个方法,结构如下:
构造方法解析传入的字符串,并调用initCoefficient和initExponent方法初始化系数与指数,derivation是求导方法,更新当前的指数与系数。
isSymbol方法是判断一个字符是否为'+'或'-'符号。
get方法获得相应的指数与系数值,add方法在当前系数上增加,合并同类项的时候会用到。isConstant方法判断传入的字符串是否为常数项(后废弃)。
getResult方法获取结果字符串,我也不知道为什么它不叫toString
一个多项式类Polynomial,包含一个单项的arraylist和6个方法,具体结构如下:
构造方法解析传入的字符串,并初始化arrylist(甚至还包括了异常退出和合并同类项)。derivation方法对每个单项进行求导。
exitException方法在检测到异常格式时调用,退出程序。
sort方法将arraylist按指数降序排列(强迫症),adjustSequence方法将第一个系数为正的单项调整为第一项。
getResult方法,依次调用单项的getResult方法。
Main类处理输出与输出,并用try,catch块包含起来,处理可能的异常。
优点:对可能的非法输入进行了完备的判断,满足了本次作业的需要。
缺点:方法结构只具有一个简单的雏形,且大量使用可变对象,不易维护与扩展。
强测与互测:没有出现问题,只是优化方面有一种特殊情况没有考虑到。
第二次作业:
包含了7个类:没错我重构了。
类图如下:
度量分析如下:
改进之处:将字符串预处理提取为preTreat方法,将合并同类项提取为mergeSelf方法,将getResult方法重命名为toString。
优点:1、实现了对应的功能与简单的优化功能
2、支持引入新的初等函数的扩展(只需更改底层Variable类及求导方法即可),可惜下一次作业没用到。
3、新增了ExitWithException类,内含静态方法作为检测到非法格式的统一出口,便于管理。
缺点:1、不利于嵌套层面的扩展,想必你已经知道之后会发生什么了。
2、本可以使用Hashmap,更加高效。
3、仍不可避免地混有一些可变对象。
强测:优化方面仍有待改进。
互测:一种非法格式没有判断出来,在Poly类的pretreat方法中。
第三次作业:
终于来到了重头戏第三次,在贴图之前我先简单说一下架构过程(1.0版本):
因子层面:新增幂函数因子、三角函数因子、嵌套因子类,每个类实现derivation与toString方法,并让Factor类统一管理因子类。
单项层面:仍然是一个因子的arraylist。
多项层面:依然是一个单项的arraylist。
解析层面:新建RecurDescent类,进行递归下降解析,最终得到一个poly对象。
在架构过程中,我第一次使用了求导接口,由于是第一次使用,所以并没有发挥其真正的价值,下面我会进行分析。
类图如下:
度量分析如下:
存在的问题:处理多层嵌套括号时,效率较差,优化前十几层括号就会TLE,优化后对于简单嵌套括号可以较快处理。
强测:强测有两个测试点TLE了,还有一个非法格式没有判断出来,bug存在于递归下降类。
互测:没有问题。。。
强测结果出后,为了充分发挥接口的作用,我又一次重构了代码。
具体改动如下:
去除Factor类,并让常数因子实现求导接口,用求导接口来代替因子类。
在嵌套因子层面上、单项层面上、多项层面上加入优化,避免无意义多层嵌套的出现。
优化后终于没有在TLE了。
至此,第三次作业正式落下帷幕,虽然强测结果不是很理想。
第三次作业重构后,应该才是真正实现了可扩展性,只需要新加入因子实现求导接口,即可实现求导。
下面简短分析一下整个过程:
1、第一次作业,将单项、多项分别提取为类。
2、第二次作业,新增因子类、变量类等。
3、第三次作业,由于不需要变量类,去除了变量类,新增了递归下降类,定义了求导接口管理因子。
重构的过程中,我也对面向对象与可扩展性有了更深的理解。
寻找他人bug的方法:
1、随机字符串对拍
2、随机增加非法字符非法空格,看是否WF
3、阅读代码
个人总结:1、在经历了三次作业后,我认识到一定的优化是有必要的,一方面可以使得结果更加简短、另一方面也可以节省程序执行的时间和空间。尤其是对于java这种效率本身不高的语言,不优化于人于IDE都是一种折磨。
2、接口远比想象中要强大,配合instanceof可以获取具体类型,具体操作。