面向对象程序设计第一单元总结
面向对象程序设计第一单元总结
一、程序结构
第一次作业
图1:第一次作业UML图 第一次作业结构较为简单,工程中有6个类,其中:Poly为多项式类,Term为项类,Point为因子类,PolyCompute为求导类,Format为将输入的字符串标准化的类。我认为自己在第一次作业中已经能够使用面向对象的思想进行代码的编写。思路为首先由Format类读取处理字符串并形成一个Term类的ArrayList,再由PolyCompute类进行合并同类项以及求导。“方法和类的复杂度分析图片中ev(G),iv(G),v(G),OCavg,WMC含义如下:
ev(G)即Essentail Complexity,用来表示一个方法的结构化程度,范围在[1,v(G)]之间,值越大则程序的结构越“病态”,其计算过程和图的“缩点”有关。
iv(G)即Design Complexity,用来表示一个方法和他所调用的其他方法的紧密程度,范围也在[1,v(G)]之间,值越大联系越紧密。
v(G)即循环复杂度,可以理解为穷尽程序流程每一条路径所需要的试验次数。
OCavg代表类的方法的平均循环复杂度。
WMC代表类的总循环复杂度。”
(粘贴自https://blog.csdn.net/weixin_30588827/article/details/96159607)
图2:第一次作业类复杂度图 图3:第一次作业方法复杂度图 从Metric中可以看到,Point类中的toString方法复杂度比较高。
第二次作业
ps:由于方法复杂度分析图过于冗杂,因此在第二次作业和第三次作业的博客中,仅展示类复杂度分析,而不展示方法复杂度分析。
图4:第二次作业UML图 相较于第一次作业,第二次作业我采取了比较面向过程的方式进行代码编写(据说方法叫做递归下降),也就是按照任务指导书的定义,首先对文本进行一些标准化,再进行分析,主要建立了因子接口,接口下有多项式、项、正弦函数因子、余弦函数因子、幂函数因子几个类,并对每一个类写了求导方法和toString方法,将因子类分为了ConstFactor、CosFactor、SinFactor、PowerFactor四种类,同时增加了一个接口类Factor,以上四个类以及Term、Poly两个类都是Factor的实例类。此外,还添加TermKey类,作为优化时合并同类项HashMap的Key。将Format类更名为PolySimplify类,并且进行了重构。图5:第二次作业类复杂度图 第二次作业中有一半类的OCavg参数比较大(第三次也是),并不是很清楚是什么原因造成的,可能是因为递归下降的过程中,要不断采取递归方式进行字符串处理吧,不知道有没有什么可以避免的方法。(ps:如果有助教看到孩子的疑惑希望能帮孩子解答一下)
第三次作业
相较于第二次作业,增加了一个Format类和FormatException异常类用于进行输入格式检测,同时重构了三角因子类以及与其线管的类,由于第三次作业与第二次作业相比增加的部分几乎不需要改变架构,因此大体上架构与第二次无异。
图6:第三次作业UML图 第三次作业,主要变化有两点,一是需要增加格式检测,格式错误的输入需要输出`WRONG FORMAT!`,二是三角函数括号的内部由一定是$x$变为了可能是任意类型的因子。第二个变化比较简单,只需要重写一下两个三角函数的因子以及递归下降读取的类即可。而由于我之前没有做格式检测,而是直接对输入进行了标准化处理,因此需要额外写一个类对输入进行格式化检测,否则需要对之前写的代码进行大范围改动,很容易出现新的bug,因此我采取的策略是先对输入进行形式化检测, 如果由形式错误,直接抛出异常,否则按照第二次作业进行求导操作。图7:第三次作业类复杂度图
二、bug分析
己方bug分析
笔者在三次作业的强测和互测中均未出现bug。但是在自己调试的过程中,在代码行数比较长和引用其他类的代码的地方更容易出现bug,也更难分析bug产生的原因。因此希望自己以后在代码的编写中能够做到让代码各司其职,不要纠缠在一起。
对方bug分析
对方三次作业中出现的bug原因如下:
1、为了追求一些优化在化简多项式时出现错误,导致输出结果时错误的;
2、使用Long类型变量读取BigInteger导致程序崩溃;
3、Format检验时考虑不全面出现的bug;
4、虽然输出的求导结果是对的,但是输出的形式并不符合任务书的形式化定义,例如,在输出中出现了
x*-sin(x)
,正确输出应该为-sin(x)*x
或者x*-1*sin(x)
。
三、hack策略
1、在读完任务书要求分析可能出现bug的地方,构造数据。
2、写测评机,在互测代码下发后使用测评机对构造的数据进行测评,从而找出他人的bug。
3、阅读他人的代码,定位具体产生bug的地方以及bug产生的原因。(同时可以学习他人的代码风格或一些小细节)
四、重构经历总结
由于第一次作业只含有普通多项式,即所有的因子都可以统一化为\(a\times x^c\)(其中\(a\)与\(c\)为常数)的形式,因此采取HashMap的形式保存\(a\)与\(c\),方便合并同类项,最后求导的时候直接遍历求导即可,当然输出时有两个优化点,一是有正项因子时先输出一项正项因子,可以减少一个
+
的长度,二是x**2
输出为x*x
,可以减少一个符号的长度。第二次作业增加了\(sin\)和\(cos\)因子,并且出现了表达式因子,我第一次作业的方法不再适用于第二次(本来以为只需要扩展成四元组就可以了,没想到多了表达式因子),因此第二次作业我采取了比较面向过程的方式进行代码编写(据说方法叫做递归下降),也就是按照任务指导书的定义,首先对文本进行一些标准化,再进行分析,主要建立了因子接口,接口下有多项式、项、正弦函数因子、余弦函数因子、幂函数因子几个类,并对每一个类写了求导方法和toString方法,最后只需要输出Poly.diff().toString()就是最后需要的求导结果,当然具体实现过程中还需要进行一些化简。
第三次作业,主要变化有两点,一是需要增加格式检测,格式错误的输入需要输出
WRONG FORMAT!
,二是三角函数括号的内部由一定是\(x\)变为了可能是任意类型的因子。第二个变化比较简单,只需要重写一下两个三角函数的因子以及递归下降读取的类即可。而由于我之前没有做格式检测,而是直接对输入进行了标准化处理,因此需要额外写一个类对输入进行格式化检测,否则需要对之前写的代码进行大范围改动,很容易出现新的bug,因此我采取的策略是先对输入进行形式化检测, 如果由形式错误,直接抛出异常,否则按照第二次作业进行求导操作。形式化检测的类也成为了程序中最长的一个类,并且笔者感觉形式化检测其实也是一个按照过程的思想去解决的问题,因此在这个类中也是按照递归下降的策略按照面向过程的思想进行编写的。
五、心得体会
由于本人比较佛系,所以除了第一次作业强测得了100分外,剩余两次作业均有部分测试点得分较低。我认为经过本次单元的练习,我能够更加了解面向过程编程与面向对象编程的区别,同时掌握JAVA中一些自带的类和方法,并且更加擅长使用正则表达式进行文本处理(当然,我现在感觉正则表达式对我来说还是比较难的东西,可能未来还需要更加多的练习)。此外,我个人感觉比起性能,更重要的还是程序的正确性。虽然有人说bug是程序的固有属性,但是个人觉得为了一点性能牺牲掉程序的正确性这种事情是很不值当的(也有可能是我比较懒),所以个人还是偏向于正确性的前提下做一些比较小的优化。
对自己比较不满足的一点就是我的优化和我的读取因子的过程杂糅在了一起,虽然效果还是不错的,但是想要进一步优化的时候需要重新对字符串进行很多很多处理,个人感觉比较麻烦,所以没有去做,这是这两次作业中有点遗憾的地方叭。
总之,希望未来自己可以再接再厉(注意身体。