BUAA_OO_2022 第一单元总结

面向对象 第一单元总结

第一次作业

总体架构

  显然由于假期美赛的冲突导致了我预习的匆忙,事实上作业开始后我的Pre3还没有用过,因此在思考了三天以后,我放弃了一般读入选择了预解析模式,在半小时后完成了代码架构的设计,核心的在于使用hashmap进行多项式的储存,利用预解析的每一步进行运算不断更新这个单独的hashmap,最后得到的就是表达式的数学储存结果,最后优化输出即可。因此,本次作业的整体结构分为四个部分:MainClass 类为程序主控类,负责输入输出等主要流程的控制;Parser类负责根据字符串返回指定的hashmap,五种运算类负责对读入的运算进行处理,最后两类进行输出的字符串表达以及简化。

类图分析

img

  • MainClass
    • 负责读入字符串,对预解析处理分类并输出最后结果的流程控制。

  • Parser
    • 利用mainclass的输入返回对应的hashmap。

  • Neg,Add,Sub,Mul,Pow
    • 根据输入的hashmap进行运算。

    • 返回hashmap作为fi(label)的结果。

  • Express
    • 表达式类,实现了标签和对应表达式的字符串储存以及数据储存。由于最终结果一定是一个多项式因此一个hashmap就可以完美解决。

  • Tiaozheng,Toanswer
    • 这两类是负责对最后的express进行字符串转化以及字符串简化。

  • 优点

    • 整体上解耦设计,将代码分成流程控制、表达式计算和表达式简化三个部分。这样可以在完成代码时分步进行实现与测试,保证前一部分没有 bug 时再完成下一部分,减小了 debug 难度。(当然也是因为使用了预解析使得本体的核心难度下降)

    • hashmap作为统一的存储,这样使得可以在计算时直接数学简化,有利于后续表达式输出。

  • 缺点

    • 部分类的没有必要单独存在,比如计算类的其实可以就放在一个类当中,并且最后输出优化其实可以和基本输出放在一起。

    • 使用了预解析。

    • 没有充分体现面向对象的编程。

度量分析

img

 

上表为各方法的度量分析,从中可以看出方法整体复杂度与类的耦合度都较低,但是存在个别方法的复杂度过高。如 Mainclass 的main作为分流控制以及 Toanswer类中的 end 方法作为表达式字符串转化的主要部分,其中包含了多个 if-else 判断语句,导致其代码行数、复杂度与耦合度,控制流复杂度都较高。

img

 

上表为每一个类的度量分析,同样可以看出复杂度最高的三个类是 MainclassToanswerParser , 这是因为 MainClass 类作为计算分流控制导致复杂度上升此外Toanswer由于优化条件多较为复杂,因此内容也较多,而其他类相对简单除了乘法的计算是hashmap遍历导致复杂度上升。

程序bug分析

本次作业由于使用了预解析设计整体架构,因此在公测时出现了2个bug。一个问题是结果为0的时候没有输出,另一个问题是对于较大的常数或者系数会越界需要使用BigInteger,导致runtime error,此类错误就只能通过仔细阅读指导书和单元测试来debug。

互测和强测时,由于我在将正项提前以减小输出长度时出现疏忽,没有正确设置分割的index,而产生了错误的输出,导致在强测失分互测被hack。因此,也提醒自己在修改以后一定要针对测试。

测试策略

  • 由于第一次作业复杂度不高加之我使用了预解析,主要用使用手动构造样例以及利用指导书的样例对代码进行测试。在互测时我没有 hack 到存在问题的代码

第二次作业

总体架构

本次作业新增了三角函数、自定义函数和求和函数三个新的因子,然而没有想到的是由于预解析的代价从9折变成了8.5折因此我被迫放弃第一单元的架构,直接进入第二次作业的全部重新设计,所以要在第一次作业结构的基础上全部重新设计。由于不少同学已经有了设计的基础,加之在研讨课上同组的同学分享了hashmap的储存结构,另外在第一周周末我完成了Pre3,对于正则表达式的使用完成了掌握,因此在重新设计的过程中很快找到了门路,实现了递归下降的储存以及自底端向上的数学计算和最终的表达式输出。

类图分析

img

 

 

 

  • MainClass
    • 负责数据读入,整体流程控制以及数据输出。

  • Pre
    • 包括对空白符删去,符号化简,三角函数的符号替换,sum的展开,自定义函数的替换与展开

    • 对于自定义函数,先将其定义的表达式进行解析并储存。当遇到函数调用时,进行实参替换以及展开。

    • 对于求和函数,直接展开并替换。

  • Self
    • 自定义函数类用于储存自定义函数的函数名、变量名、表达式等性质。

  • Factor
    • 因子类。

  • Expr
    • ArrayList<Term> 来储存解析出来的项。

  • Term
    • ArrayList<Factor> 来储存解析出来的因子

  • Tri 
    • 三角函数因子,存贮类型,自变量和指数

  • Pow
    • 幂函数因子有符号和指数。

  • ExperFactor
    • Expr类似,但是有指数和符号

  • Index
    • 指数类

  • Num
    • 常数因子

  • Std
    • 标准项类,用于数学转化储存所有的因子

  • Mul
    • 计算Arraylist<Std>的乘法并返回对应的Arraylist<Std>

  • Sp
    • 提供Arraylist<Std>的化简方法,本质是合并同类项目

  • Hseq
    • 提供hashmap的深度比较,确认两者数据是否为同一储存数据

  • 优点

    • 本次作业对于括号嵌套的处理比较完美实现了统一的运算方式和因子的统一生成和储存。

    • 建立了表达式树的结构,对于自定义函数及求和函数的因子代入很早就进行了处理有利于debug观察和解耦。

  • 缺点

    • 表达式化简仅仅实现了同类项合并,对于 cos(0)sin(0) 之类的因子没有考虑化简,导致部分测试点性能分数过低,有几个点性能分为0。

    • 将大部分替换工作放在了 main 类中,导致开头越来越复杂,虽然有利于debug但是不美观。

度量分析

img

 

 

img

 

 

img

 

img

 

上表为各方法的度量分析,和第一次作业相比因为实现功能复杂性以及没有使用预解析导致代码量起飞,大部分方法的耦合程度以及复杂度以及最后的平均值都很低,这说明代码整体的架构设计还是比较合理的。

img

 

上表为每一个类的度量分析,同样可以看出复杂度最高的两个类是PreSp。这是Pre承担的替换展开任务以及方法都比较复杂,而Sp过程同样有hashmap循环遍历但是整体的 OC 与第一次作业相比却有所下降,说明了第二次设计(其实叫第一次设计)的合理性,这说明各类分工明确,符合高内聚低耦合的原则。

程序bug分析

  • 本次作业由于内容复杂度限制提升,以及第一周使用了预解析导致在一些细节地方出了不该出现的 bug,在公测中因为sum中的一个数组没有刷新中挂了两天。最终在和同学们的讨论下和自己不懈的测试下最终在距离提交4小时前得以解决

  • 而在互测时,没有被hack到反而由于自己顺手构造了数据hack了别人。

测试策略

  • 本次互测我由于自己测试的过程一直有所记录,因此我把自己走过的弯路让别人的代码也走走,其中使用sin(-1)成功hack到三人。

第三次作业

总体架构

第三次作业减少了上一次作业中因子的限制,因此需要修改的部分较少,又因为第二次作业中实现了无限括号嵌套,因此几乎没有大的改动就完成了本次的代码。

类图分析

img

  • Pre
    • 由于自己在第二次作业中使用了括号嵌套(结合栈的使用)因此只需多次调用处理函数就可以实现多层括号嵌套的处理,因此只在其调用处设置循环即可。

  • Std
    • 由于三角函数内部因子也可以为表达式因子,因此需要添加将三角函数内部Zbl的视为表达式因子,这可以通过已有的方法实现。

  • 优点

    • 体现出了良好的扩展性。

  • 缺点

    • 仍然没有将 sin(0)之类的 替换成 0并且没有将sin((1))化为sin(1) 等。

    • main函数过于长

度量分析

img

 

img

 

img

 

img

上表为各方法的度量分析,可以看到基本与第二次作业一致。

img

 

 

上表为每一个类的度量分析,其中Pre 的复杂度进一步增加,mainclass复杂度起飞,但逻辑上其实没有太大的变化(只是加了循环),std由于改了tri类型的转化因此也少量上浮。

程序bug分析

  • 由于本次作业的增量较少,所以在公测时出现的 bug 也比较集中。一个发现的 bug 在修改自定义函数展开时候忽略了h类型的自定义函数,另一个就是嵌套计算Tri.Zbl时候需要对其进行表达式简化防止出现重复存储的bug

  • 互测和强测出现了两类型的bug,一个是自定义函数中‘,’不再作为当前自定义函数的自变量划分的充分条件,因此要使用栈去判定,二是sum函数中对于上下限int无法满足,要替换成大整数。

测试策略

  • 由于自认为架构比较完善延续性好没有做过多测试,在测了简单的嵌套(形参只调用了一个)以后,没有继续测试,结果出现了上述的问题。

架构设计体验

通过第一单元的作业,让我领悟到了架构设计的重要性,以及何为层次化设计。虽然最早没有使用一般解析,但在压力的驱动下自己成功构造了设计,并且在第三次作业中体现出良好的延续性。同时第一次作业启发了我将字符串的加减乘转化为数据类型的加减乘,其中标准项的思想对于我第二次作业的设计有很大的帮助。把问题转化为抽象的数学问题往往可以简化程序,而将大问题转化为小问题有利于提升代码编写的信心,提高编码和debug的效率。

层次化设计方面,由于递归下降的引入以及pre2中的指导,在第二次作业中很快体现了其强大的组织建设数据的能力,我也迈入了面向对象的编程,使得编写的内容非常有针对性,降低了编码的盲目性。

心得体会

最早预习的不充分在第一周深深折磨了自己,然后在第二周直接让我爆肝一周,也算是明白了预习设置的目的以及重要性。好在同学们都很积极乐于帮助他人解决问题,遇到坑点难点大家相互帮助,挂同一个测试点的同学相互讨论,研讨课上的热烈讨论和方法的提案都让我受益匪浅,多交流多学习是我目前觉得OO带给我很不一样的体验。同时助教学长学姐的及时答疑以及bug修复时申请反馈让我感受到课程组处理事务的高效。当然很重要的一点就是oo很苦,但是完成以后的成就感确实无与伦比的,使用一周的时间就会让你能感受到蜕变和进步。同时staruml,git等工具的使用也是oo交给我很重要的一课。总之,第一单元结束了,但是我们的路还在继续。


posted on 2022-03-23 19:37  Lzchhh  阅读(86)  评论(2编辑  收藏  举报

导航