2020北航OO第一单元总结

前言

学习面向对象这门课程的后的第一单元做业,主线是多项式求导,三次做业层层推动,由单一的幂函数求导,到幂函数和三角函数的复合求导,最后再到两种函数的嵌套求导,由两个类到重构后的十几个类,我逐渐对面向对象的思想有了更深一步的理解,对结构化的设计也有了更加深入的体会。正则表达式

第一次做业

做业要求

实现仅含幂函数和常数的多项式求导,数据长度上限1000,性能上要求结果越短越好(即化简到最简),保证输入数据合法。设计模式

实现简述

完成本次做业时,因为扩展意识不足,采起了仅为解决当前问题的设计模式,包含两个类PolyPolyComputer,其中Poly类用一个HashMap来存储多项式每一项的系数和指数,使用BigInteger来管理系数和指数,用PolyComputer类读入字符串并进行处理,并在其中直接对求导结果进行输出,如下是个人类图分析svg

 

优缺点评价

  • 优势函数

    • 因为保证输入数据合法,在dispose处理字符串时先去掉空格并对先连的正负号进行合并,使得在parsePoly中利用正则表达式解析字符串更加简便工具

    • 在用addTerm增长项时利用DegExist判断当前指数的项是否存在,存在就直接合并,作到了及时合并同类项post

    • printDeva输出时对系数和指数为0、±1时进行了特判,使输出结果达到较简的形式性能

  • 缺点学习

    • 在输出时的特判状况较多, 易出错测试

    • 直接对多项式进行求导,没有存储求导结果,使得printDeva与其它模块的耦合性过强,代码质量低优化

    • 因为前期对字符串进行处理后再进行正则判断,没法适应后面须要判断格式的改变

    • 未对函数设置专门类,致使代码在遇到多种函数复合求导时须要重构,扩展性低

bug分析

本次做业中,因为我在判断输出是失误,致使>1时才输出指数,由于这一个失误下的大bug,在强测及互测阶段我被hack得很惨。固然我也进行了深入的反思,因为第一次做业对规则还不是特别熟悉,本身在课下并无作好充分的测试,才致使了这一bug苟过中测残留到强测及互测阶段。

在对别人的代码进行测试的过程当中,我通常是构造边缘数据进行测试,如系数及指数为±一、0,连续几项相同指数须要合并,常数单一项等的状况,而后再读别人的代码进行针对性测试。最后,也成功几回hack到了别人。

第二次做业

做业要求

在第二次做业中,增长了三角函数的求导及f(x)*g(x)的复合求导形式,但三角函数因子只能为sin(x)和cos(x),同时,也要求对输入数据进行格式判断。

实现简述

在实现本次做业过程当中,因为上一次代码扩展性太差,且考虑到下次做业会更加复杂,我选择了及时重构。重构时的思路主要分为如下三个方面:

一、结构化层次关系的设计

首先,我定义了Factor类,设置系数和指数属性,在里面定义了一些因子共有的特征及加和乘的运算,而后使幂函数PowFunc和三角函数TriFunc 继承自Factor类,在每一个子类中重写derivation()toString()方法,在三角函数内设type属性存储三角函数类型。

而后,考虑到本次做业的特征,每项最多由幂函数*正弦函数*余弦函数的格式组成,故在Term类仅设置三个因子对象作为属性,并对无参数的构造方法初始化为系数为1,指数为0的形式,同时,为了将每项系数合为一块儿,我将幂函数的系数默认为项的系数,在每一项被加入到多项式时利用combineCoef()方法将三个函数的系数合并。

最后,将原来的Poly类内由存每项的系数和指数,改成存多个Term的容器,而且在addTerm()时利用isCoefZero()及时删去了零项。

总之,在对象的建立上,我总体实现了由factortermpoly的层次结构。

关于求导方面,我利用三层结构的迭代求导,在Term类内的derivation()方法中根据​f(x)*g(x)的求导规则进行特殊书写。

二、输入数据的格式判断及解析

对于Wrong Format!判断,我自定义了InputException()的例外,并在其中设置printError()方法,在检测到非法格式时抛出例外。在Main类中进行try-catch的捕捉。

因为上次做业我先对表达式进行处理,致使没法判断输入数据格式,在本次做业中,我将原来的PolyComputer类改成StringHandler类,进行输入数据的判断和处理,并在正则表达式中补入了空格,先根据正则表达式检测数据格式,而后用dispose()方法对表达式进行处理,最后以项为单位解析表达式,存入一个Poly对象中。

三、关于优化

此次的做业若是利用各类三角函数的公式,其实有不少点能够进行优化,可是不少时候实现并不容易,且容易引起不少未知bug,何况不少优化规则的实例出现几率很小,故我最后只在如下三个方面进行了优化。

  • ​sin2(x)+cos2(x)=1

  • 同类项合并

  • x**2x*x

最后,我重构后的代码结构类图分析以下:

 

其中,StringHandler的循环复杂度较高,但由于涉及到字符串的解析,因此我认为是没法避免的。

优缺点评价

  • 优势

    • 结构层次较为清楚,有必定的可扩展性

    • 各种功能简单,不易出现bug

    • 实现了简单的优化

  • 缺点

    • Term类的属性处理不当,致使后面在增长多中因子时须要重构,改成存储Factor的容器

    • Poly中进行优化时涉及到多个类的方法调用,耦合度较高

    • StringHandler处理字符串时,生成项判断能够利用工厂方法来进行解耦,也会使思路更加清晰

bug分析

因为吸收了上一次的经验,以前进行了自我测试,并写了自动化测试工具进行测试,在强测及互测阶段,个人程序并无被hack到,但我也知道它可能在某个神奇的地方仍存在bug

与此同时,在测试别人代码时,我偷懒使用了自动化测试工具, 而且因为当时在忙一些其它的事情,我没有时间去分析完每一个人的代码,本身构造的一些测试点也没有hack到别人,最后自动化测试也不争气,我体会了惟一一个和平的互刀环节。

第三次做业

做业要求

本次做业主要增长了三角函数的嵌套求导,并增长了表达式因子

实现简述

一、结构层次关系的设计

因为增长了表达式因子和嵌套的三角函数,我增长了NestFactorExprFunc两类,其中ExprFunc继承自Poly类,将原来的Factor父类改成存储变量因子的VariableFactor,并使全部的因子实现Factor接口,实现求导、加乘等一系列因子的基本操做。

在处理嵌套因子时,因为嵌套的求导规则是对每层进行求导并相乘,在这里,为了防止爆栈,我在NestFactor用了一个Factor的容器,从外向内存储嵌套每层因子,求导时只需对容器的每一项进行求导,对于三角函数括号内的部分在TriFunc中定义factor字符串直接进行存储,在输出时代替原来x的位置便可

具体的UML类图以下:

二、输入数据的格式判断及解析

在输入数据的格式判断,因为这次为含递归的正则表达式,不能简单用正则表达式直接判断,在此,我新建了一个FormatCheck类,对输入表达式进行递归判断,并专门写一个findRightBlacket()的静态方法,返回与当前字符串最左侧括号所匹配的右括号位置。

在解析表达式时,我新建了一个PolyHandler类,将表达式以项为单位传入Term中,循环填充一个Poly类的对象,同时,为了减小类之间的耦合度,我将格式检查也在这里进行。具体的关系以下图:

在解析表达式时,吸收上一次的经验,我新建了FactorFactory的工厂类,把解析出的因子表达式传入生成相应类型的Factor对象

在下面类度量中,能够发如今,还是在含表达式的解析的类循环复杂度比较高

三、关于优化

  • 实现了部分同类的因子和项之间的合并

  • NestFactor中,经过重写的toString()对嵌套因子内部的字符串进行循环替换,简化了含前导0、符号多余以及因子之间可合并的地方

  • x**2x*x

优缺点评价

  • 优势

    • 表达式的解析分为几部分在类的内部执行,减小了表达式解析的循环深度

    • 迭代求导部分思路比较清楚

    • 嵌套因子的处理较为简便

  • 缺点

    • 因为三角函数可被归为ExprFuncTriFunc两类,同类项合并时很差判断

    • 因为表达式因子的存在,使得因子求导的返回必须是Poly类对象,对于VariableFactor类,其实返回Factor类就够了,但还须要把他们一步步转成Poly类,以为较为冗余,但也没有好的办法解决

    • 在对求导结果进行复合时,须要分类处理,不能作到很好的归一化,不然会出现神奇的bug,感受有点麻烦,应该还有解决办法

bug分析

此次做业太过复杂了,果真最后出现了一些很神奇的边缘bug,虽然很好改,但我被hack得很惨

  • 在表达式因子求导获得Term类对象后,我将它toString()的结果传入了Term中进行新的一项的解析,但因为我将x**2转化成了x*x,会致使传入sin(x*x)类不合法因子,而我并未对此类因子进行解析,因此最终会出现RuntimeError

  • 在因为优化, 输出时可能会有sin(x*x)类不合法输出格式的因子

  • 因为我在TriFunc类里的toString()方法内直接选择对factor为0的因子输出为0,致使有cos(0)时会出现错误

在测别人的bug时,我针对sin(0)**0cos(0)等类型的易错点设置了测试样例,果真也hack到了一波别人,同时也测试了多层括号嵌套、连续符号判断等状况

总结

在此次做业中,我对面向对象的思想有了更深的理解,也更加意识到了层次化设计的重要性。一次重构的痛苦经历,也给了我足够的警醒,在之后的做业中,我会在一开始就考虑更具可扩展性的设计。

相关文章
相关标签/搜索