OO第一单元作业总结
说来惭愧,我这三次作业基本上都是面向了过程,用到类也基本上是当成结构体来用,继承多态都不存在,这也让我不得不感到自己对这门课程内容的应用实在是太少了,学了一些理论知识,却都没有运用到实践中。因此在三次作业完成之后,在这周进行总结任务的同时,我也开始反观自己的代码,试图对第三次作业进行重构,但到目前为止还没有改完。
一、度量分析:
第一次作业:
大体思路:main基本上把啥活都干了,Poly类负责存储分割表达式得到的每一项的系数和指数,具有求导功能。
类图:
复杂度:
第二次作业:
大体思路:跟第一次作业类似,main做了很多工作,存储和求导方式跟多数人类似,也是将每一项都看成固定的一类Poly,其内部有长度为3的Differential类数组,Differential为项的导数。
类图:
复杂度:
第三次作业:
大体思路:比前两次略有进步的是这次main没有实现太多的功能,只是简单的输入和输出,但就类的使用方面来看还是十分面向对象。Item类为+-号分割开的各项,Expression类为*号分割项得到的因子,Expression和Item不断递归调用彼此拆分原表达式直到不能再拆分,得到最终结果。
类图:
复杂度:
可以发现,由于没有建立不同的类对每一种函数进行拆分,而是直接在Expression类和Item类里做大量的操作和递归,导致复杂度很高。
二、作业中出现的Bug
第一次作业:
第一次作业时的bug主要在非法空白字符的处理方面,当时没有在判断格式的正则表达式里使用[ \t]这种只包含合法空白字符的形式去判断输入字符串的合法性,而是希望使用类似特判的形式去寻找输入字符串中不符合规则的非法空白字符,相当于是判断非法性。
然而合法字符毕竟是有限的并且是已知的,相比之下非法的则是有很多而且未知。判断合法性比判断非法更加容易也更加不易遗漏。
第二次作业:
第二次作业的bug是优化时产生的。优化时也没多想,出了很多类似直接将所有的sin(x)^1替换成sin(x)这种愚蠢的错误,由于自己是先得到结果再优化结果,所以这个bug很容易就可以发现,也比较容易改正。
第三次作业:
第三次作业的bug主要出现在表达式拆分为项的环节。在确定拆分表达式的+、-号时,只关注了括号,采用了类似出栈入栈的方式将括号外的+、-号作为划分标准,而忽略了*有符号数字这种情况,导致了错误划分。
三、测试策略:
一开始我希望通过阅读他人代码寻找bug,但这实施起来太困难了,好多类,奇怪的命名,几百行的代码,读懂一个人怎么写的就已经很折磨人了,于是我转变了思路,从自己设计时遇到的难点入手,来测试同组成员的代码中有没有解决我曾遇到的问题。
在前两次的作业中,错误格式的输入是寻找他人bug的利器,错误格式的输入又可大体分成符号(+-*^)问题、空白字符问题,这些问题都可以通过分析代码的正则表达式来寻找bug,特别是在第一次作业,由于考虑不全面,十分容易出错。
工欲善其事必先利其器,bug的查找也可以借助很多工具。比如生成测试数据,再比如借助脚本工具同时显示所测试不同代码的输出结果。这些在之前我都没有接触过,经过讨论区们大佬的介绍与分享,自己也很有收获,知道了借助工具来实现某些功能。
四、第一单元三次作业分析总结:
接触OO的第一个单元,自己还尚未建立起面向对象编程的思想,还停留在过去面向对象的阶段。我也正在努力重构自己第三次作业的代码,希望通过重构重新审视问题。
这几次作业,特别是前两次作业自己程序的功能几乎都是在Main函数里完成的,到了第三次作业才一点点改善,但这还远远不够。自己应当多进行模块化设计,将不同功能分成不同的模块,互不干扰,比如这单元的作业可以将程序分为输入、功能实现、优化、输出几个模块,更不易出错,也更容易进行复用。
通过回顾的第二次作业和第三次作业,发现了自己所写的不同类之间往往有一些相似性,特别是方法的实现,可以考虑通过继承的方式实现方法的复用,而不是在每一个类里都写一遍。
第三次作业目前的重构想法:
之前自己的项类Item分割成因子之后就直接假定其为表达式,又进入表达式类继续拆分,我想通过设置不同的因子类来进一步分解,加一步因子类的过程。
在项类和表达式类中都判断是否为数字或者X型,进而递归终结,而且这两个类使用类似的方法,可以考虑继承。
表达式、项和因子都需要具有不同的求导功能,可以设置求导接口。
将程序分为几个模块,实现输入、求导、输出几部分的独立。