2021_BUAA OO第一单元总结

OO第一单元总结

第一次作业

一、题目要求

简单多项式求导,仅支持常数幂函数的乘积作为项,保证输入无格式错误。

 

二、作业实现及分析

由于第一次作业要求实现的功能并不复杂,且我当时对于面向对象的理解并不够深入,所以我当时只是根据自己的想法构建了4个类来解决这个问题

类图如下:

 

关键类分析如下:

  • Main —— 主类,具体实现以下功能:
    • 从输入读取字符串并存储
    • 对得到的字符串进行初步的处理并传入StringProcess类中以生成Term类的实例
    • 将得到的Term类的实例存入TermMap
  • StringProcess —— 字符串处理类,具体实现以下功能:
    • 它的一个内部属性为字符串
    • getStdTerm()方法是为了将该字符串转化为Term类的一个实例并返回
  • TermMap —— Term类实例的存储类
  • Term —— 存储某个项信息的类

复杂度分析如下:

  

 

 

  •  整体代码量
    • 主函数代码量不算多,但是还是没能做到真正的小巧简单和可读性强
    • 其他3个类代码量分布较为均匀

 

 

  •  类复杂度
    • Main类的复杂度爆红,原因在于其中进行了过多的字符串处理(正则查找、split分割……)以及if-else语句
    • TermMap类主要是其中的printTermMap()方法有些复杂,在后文会提及

  • 方法复杂度
    • 其中Term的toString()方法的圈复杂度爆红,其主要原因在于对输出的优化时采用了大量的if-else结构,对于很多特殊情况进行了判断。这样感觉很容易出现很难找出的问题,并且代码的可读性很差
    • 同理,TermMap的printTermMap()方法也是因为采用大量if-else结构导致圈复杂度过高
    • 并且以上的两个方法也存在着耦合度较高的问题,这会使得代码难以隔离、维护和复用

总结如下:

  • 出现以上的问题感觉是面向过程的思想在作祟,例如main函数内内容过多等,希望在后续学习中可以渐渐掌握面向对象的思想,能够写出更清晰明了和高效的面向对象代码

 

三、Bug与性能

Bug方面:

  • 本次作业中我出现了重大的失误,因为开始着手处理作业的时间太晚,导致我没能按时通过公测
  • 由于代码写的过于匆忙,在强测中我出现了两个低级错误:一是求导结果为0时,我的程序输出为空;二是在TermMap的printTermMap()方法中,由于将for循环中的条件写错,导致在一些情况下会连续输出两次系数导致错误
  • 这次重大失误给了我一个教训,今后我一定认真完成作业,不再会犯类似的错误

性能方面:

  • 性能方面表现良好

 

四、优缺点总结

优点如下:

  • 代码总体逻辑较为清晰

缺点如下:

  • 没有用面向对象的思想去处理问题
  • 代码可扩展性很差

 

 

第二次作业

一、题目要求

在第一次作业的基础上,增加带幂次的正弦 sin(x) 和余弦 cos(x) 函数,且支持带有括号的表达式作为乘法因子

 

二、作业实现及分析

第二次作业相较于第一次作业,在难度上有了很大的提升:由于多项式因子的存在,我们不再可以只用Term一个类来描述所有的项了。于是在本次作业中,我选择了重构——将输入的表达式分为多项式、项、因子3级,用递归下降的思想来处理输入的字符串,最终将表达式以表达式树的方式存储起来并对树进行求导等操作。

类图如下:

 

关键类分析如下:

  • Main —— 主类,主要实现以下功能:
    • 输入字符串的初步处理
    • 运用递归下降的方法建立表达式树
    • 将求导得到的表达式树以字符串形式输出
  • Expr —— 表达式树的结点类,其有SingleExpr、AddExpr、MulExpr、SubExpr共4个子类,通过这些不同种类的节点类型可以构建出一个表达式树,且子类中都有相应的求导等方法

复杂度分析如下:

 

 

 

  • 整体代码量
    • Main类的代码量过多,原因在于其中有字符串处理和输出的部分,事实上main函数的代码量只有10行左右
    • 在节点类中,SingleExpr中的功能最为重要和复杂,因此其代码量偏多,其他节点类的代码量均较少
    • 总体的代码量较少

  

 

  • 类复杂度
    • 类的复杂度普遍偏低,这体现了所采用的递归下降表达式树相结合的方式可以较为简洁地处理该问题
    • Main类的复杂度相对较高的原因是我将对于字符串的处理和输出方法放入了Main类中,其实应当将这些方法提取出来,并用额外建立的一个方法来承载

 

 

  

 

  • 方法复杂度
    • 由于在Main类的Output()方法中需要判断的情形较多,所以使用了较多的if-else语句,这增加了代码的复杂度
    • 对于SingleExpr类的getDerExpr()方法,其由于需要分为常数、幂函数、三角函数等情况讨论,因此代码的复杂度也是较高,并且该方法还存在着耦合度较高的缺点,这会使它难以隔离、维护和复用;为解决该问题,可以考虑根据这些不同的情形分别建立SingleExpr类的子类,以此来降低该方法的复杂性

总结如下:

  • 第二次作业在代码复杂度方面相较第一次作业有了一些改善,但还是没有做到完全的规范
  • 此外,在问题的处理过程中我也开始采用面向对象的思想来处理,但本题还可以进一步采用工厂的方法来管理节点类
  • 事实证明,采用好的解题算法和数据存储结构可以减少代码的复杂度

 

三、Bug与性能

Bug分析:

  • 我的Bug
    • 本次公测和强测中均未出现错误
    • 但是在互测中出现了一个Bug:我的输出可能会出现多个加号,虽然结果正确,但是不符合输出的规范;于是我将输出结果中连续的加号替换为一个加号,解决了这个问题
    • 在测试中,我有如下的一些体会:由于很难对我们的代码做覆盖性测试,我们可以对于每个类分别进行测试:例如我在完成建表达式树这一部分后,就开始测试这部分内容的正确性,确保大概率正确后,在进行下一步代码的构建。不要把代码正误的不确定性堆叠到最后,不然可能会出现一些很难找出来的Bug
  • 别人的Bug
    • 当我的测试数据出现大量括号嵌套时,有一些同学会出现错误,可能是解析过程过于复杂导致的CPU超时
    • 有些同学可能忽略了三符号相连的情形(例如 -+- 的情况)

性能分析:

 

  •  本次性能优化方面我做得较差,性能分较低
  • 经过思考,我觉得原因在于我没有采取拆括号分解为不带括号的各项再合并同类项的处理方式
  • 但是我也进行了一定的优化:例如在建立加法、减法、乘法节点时我会先进行判断,这样可以规避例如 0乘x 这样的无意义的节点的出现

 

四、优缺点总结

优点如下:

  • 代码实现了好的可扩展性
  • 代码总体逻辑较为清晰
  • 代码的实现过程较为简洁,开始采用面向对象的思维方式来处理问题

缺点如下:

  • 输出的优化并没有做到位,对于一些样例的输出字符串会很长
  • Main类中的内容过多

 

 

第三次作业

一、题目要求

在第二次作业的基础上,新增了 三角函数内嵌套因子的多项式求导 和 输入语句格式的正确性判断

 

二、作业实现及分析

第三次作业相对于第二次作业来说,在难度上的提升较为平和。为了处理三角函数内嵌套因子,我新增了CmpExpr节点类;为了处理输入语句的正确性,我新增了CheckFormat类

类图如下:

 

关键类分析:

  • 由于第三次作业延续了第二次作业的架构,类的基本结构也基本相似,下面只描述与第二次作业的增加代码
  • CheckFormat —— 格式检查类,主要实现以下功能:
    • 根据指导书中的规范遍历输入字符串,遇到不合法之处就报错
  • CmpExpr —— 内嵌套因子的三角函数节点类,继承自Expr类

复杂度分析如下:

 

 

  

 

  • 整体代码量
    • 代码分布较为合理
    • 感觉CheckFormat类的内容和Main类的内容在递归下降部分有一定的代码相似性,应该可以合并这两个部分的代码,从而可以进一步减少代码量

  

 

  • 类复杂度
    • 对于Expr类和Main类的高复杂度,可能是在解析表达式时,一个方法做了太多的事,抽象层次不好,导致模块化差、耦合性高
    • 可以考虑建立一个新类用于存放Expr类和Main类中的方法,这样可以减小耦合度

 

 

  

 

  • 方法复杂度
    • 相较于第二次作业,方法的复杂度有所升高
    • 原因在于相较于第二次作业,第三次作业做了更多的优化;CheckFormat类中的方法在递归下降中出现了较多的if-else语句;最后需要解析的内嵌套因子的三角函数也更加复杂。这些都增加了代码的复杂度
    • 此外,还存在代码之间耦合度较高的问题

总结如下:

  • 当时在增添第三次作业的代码时,确实对于代码复杂度的问题并没有太注意,在后面的作业中会多加考虑
  • 第三次作业中,我确实感受到了一个拥有好的扩展性的代码的巨大优势:在后续扩展时可以节省大量的精力

 

三、Bug和性能

Bug分析:

  • 我的Bug
    • 这次在强测和互测中共被测出4次Bug,这4个Bug均错在一个点上
    • 错误原因:此前说过,我将输入的表达式分为多项式、项、因子3级,我在多项式的层面分析时没有先判断多项式是否以 + 开头,这导致了程序出现了漏洞
  • 别人的Bug
    • 发现 cos(0*x)**2 会有人出错,可能会有人在解析时出现错误

性能分析:

  • 这次进行了进一步优化,性能分相较于第二次作业有了一定的提高
  • 但是当时依然没有意识到括号展开对于性能的影响,所以性能分还是没有能提到很高

 

四、优缺点总结

优点如下:

  • 代码的可扩展性在这次的作业中得以体现
  • 树的存储结构为优化提供了更多的可能

缺点如下:

  • 复杂度的问题需要考虑到
  • 自己的测试并不到位,应当学着自己写一写评测机(之前只是使用别人的评测机来生成,感觉数据强度不是很满意)

 

测试数据的策略

  • 在查找他人Bug时,我采用的方法是人工构建评测机自动生成相结合
  • 第二次作业中:
    • 当我的测试数据出现大量括号嵌套时,有一些同学会出现错误,可能是解析过程过于复杂导致的CPU超时
    • 有些同学可能忽略了三符号相连的情形(例如 -+- 的情况)
  • 第二次作业中:
    • 发现 cos(0*x)**2 会有人出错,可能会有人在解析时出现错误
  • 另外,我也通过下载他人代码,分析他人思路的方式来找出漏洞,并有针对性地给出测试数据

 

重构经历 

本次作业中,我在第二次作业中进行了一次重构;而这次重构也算是比较平稳,并未带来太多的Bug,我想也是因为第二次作业之后,我处理问题的算法和思想更加简洁明确;而第三次作业也是衔接得较为流畅,并未遇到太大的阻力。

 

心得体会

  • 在这三次作业中,我认为第二次作业是最具挑战性的,主要是增添了表达式因子的处理,总体构思花费了我不少时间
  • 我认为这三次作业在很大程度上提高了我的编程能力,增强了我对于面向对象思想的理解,同时也让我的debug能力得到了提高
  • 在这三次作业中,在互测中通过阅读他人的代码,我也学习到了很多优秀的思路,开拓了眼界
  • 在这些训练中,为了取得更高代码风格分,我的代码规范意识也在不知不觉中得以提高,也更加注重代码的可读性、简洁性和美观性
posted @ 2021-03-30 17:34  for_wheat  阅读(72)  评论(0)    收藏  举报