BUAA_OO_第一单元作业总结

BUAA_OO_第一单元作业总结

总体概述

​ 本单元的主要任务为表达式化简,分三次作业迭代开发出一个可以化简一个包含自定义函数,求和函数,三角函数,幂函数与整数的程序,在迭代开发的过程中体会面向对象的开发思维。

第一次作业

1、题目概述

通过对表达式结构进行建模,完成单变量多项式的括号展开,初步体会层次化设计的思想。

因子:幂函数、整数、表达式因子
项:由乘法运算符连接若干因子组成
表达式:由加减运算符连接若干项组成

2、类图

3、重要类、方法分析

本次作业使用递归下降法进行解析字符串以及计算化简表达式。

Parser

利用Parser进行词法解析递归下降处理字符串,将字符串解析成一个待化简的Expr类。

Expr和Term

利用Parser将字符串转换成由加法连接项而组成的表达式与由乘法连接因子而组成的项。在项与表达式中实现calculate方法实现递归下降计算表达式,最终计算结果保留在存储基本运算单元Monoexpr的容器Monoexprs中。

Monoexpr

基本运算单元,保存最简单的单项式,即整数乘幂函数的形式,并提供了加法和乘法运算方法。

最终,利用toString方法将经过化简运算的表达式输出。

4、性能分析

代码量分析

本次作业总代码量为413行,因为没有把词法解析与拆分字符串的类分装成两个类导致Parser类行数偏高,不过总体代码量不大,其余类代码量较平均。

类复杂度分析

可以看到除了Parser类中实现了递归调用解析的方法导致该类复杂度偏高之外,整体复杂度较低。

5、优缺点分析

优点分析

本次作业整体架构良好,类之间的耦合度较低,符合高内聚低耦合和设计思路,为后续迭代开发留出了较大空间。利用递归下降法解析字符串性能较高,在计算过程中边化简边运算提升整体效率,即便是较为复杂的式子也能快速得到答案。

缺点分析

在解析字符串时没有设置成Lexer与Parser两个类,导致Parser类较臃肿,后续迭代中仍会在该类下继续开发导致发生问题的可能性上升。

6、优化与Bug总结

优化

合并同类项
x**0 -> 1
x**1 -> x
x**2 -> x*x
将表达式中的第一项设为非负的,消除第一个负号

Bug

本次作业在强测与互测阶段没有被找出Bug,与同学进行对拍中也没有找到问题,说明本次作业可靠性较高。

第二次作业

1、题目概述

在第一次作业的基础上增添了自定义函数,求和函数和三角函数,本次作业的三角函数仅允许因子为整数或幂函数。

自定义函数:f(x, y, z) = 函数表达式,由输入给出定义
求和函数:sum(i,s,e,表达式),表达式中含有i时要将i求和时累加
三角函数:本次作业仅要求sin和cos,且括号内仅能包含整数或幂函数

2、类图

3、重要类、方法分析

本次作业在上次的基础上增添了sin、cos、function与sum类,并对上次作业的部分类进行了修改与扩充。

Sin

将sin和cos作为计算单元加入Monoexpr,但由于写代码时先实现了sin类,导致没有使用继承而是重写了cos类,使得cos与sin中存在大量相似方法,形成了赘余。

Function与Sum

处理自定义函数与求和函数我都是采取了字符串替换的策略,因此放在一起总结。在解析字符串之前将字符串中对应的自定义函数和求和函数利用Function和Sum类全部替换掉,进而可以利用第一次作业的方法解析字符串

Monoexpr

因为本次作业的计算单元中添加了三角函数,因此将第一次作业的Monoexpr进行了扩充,添加了sin容器和cos容器, 并在合并同类项逻辑中加入了判断三角函数是否相等的方法,加法与乘法中也加入了对应的计算方法。

其余类与第一次作业大抵相同,不做赘叙。

4、性能分析

代码量分析

本次作业总代码量为988行,因为没有对sin类和cos类提取公共的属性和方法,导致本次代码量比预计略大。除去这一因素整体代码量处于合理水平。

类复杂度分析

本次复杂度与上次作业相比,MainClass类与Monoexpr类出现了复杂度偏高的情况,MainClass类中因为将获取并解析自定义函数的方法在该类中实现了,导致出现类平均复杂度较高的情况,而Monoexpr类因为比较和计算方法中加入了三角函数容器,导致总复杂度偏高。

5、优缺点分析

优点分析

由于上次作业的架构较好,本次作业并没有选择重构,而是在上次作业的基础上进行迭代开发,因此总工程量与总思维量并不算高,这次也较容易的完成了作业。

缺点分析

本次作业中没有很好的履行只增不改的原则,使得代码之间耦合度上升,减少了进一步迭代的可能,也导致MainClass与MonoExpr类中出现了复杂度偏高的情形。本次作业为了贪图简单,对于三角函数类没有直接储存表达式,而是存储了整数和幂函数,导致不得不专门重写了一个较弱的解析方法,也不利于下一次作业的扩展开发,导致了下一次作业对这一部分不得不进行重构。

6、优化与Bug总结

优化

本次作业沿用了上一次的优化,除上次作业的优化外,本次作业新增了部分对三角函数的优化

sin(0) -> 0
cos(0) -> 1
sin()**0 -> 1
cos()**0 -> 1

Bug

本次作业在互测环节中被发现了一个bug,三角函数中整数大于int类型时会导致数据溢出。这是也开发时偷懒导致的,如果选择在三角函数中存储表达式而不是整数与幂函数,则不会在选用数据类型时遗漏大整数这一情况。

第三次作业

1、题目概述

本次作业在第二次作业的基础上,要求实现自定义函数的嵌套以及三角函数的嵌套

2、类图

3、重要类、方法分析

本次并没有新增新的类,仅对部分类进行了修改。

Sin

为了实现嵌套本次换用了expr对sin类的因子进行储存,因此把所有三角函数相关的方法都进行了替换。为了保证同一计算结果的三角函数能进行比较,将三角函数间的比较换成了对expr的toStrng方法所得的字符串进行比较,由于toString方法中采取了对Expr内monoexpr的化简与排序,可以保证只要结果相同,得到的字符串一定相同。

Function

为了实现自定义函数的嵌套,将第二次作业中直接使用正则表达式对函数进行解析的方法改为了逐个字符分析的方法。

Simplify

为了实现三角函数的化简,在Expr中新增了Simplify方法,将计算后得到的monoexpr容器进行有关三角函数化简的运算。

其余类与第二次作业相同。

4、性能分析

代码量分析

本次作业代码量总行数为1218行。因为上次作业中未对sin与cos进行公共属性与方法的提取,本次作业在上次作业的基础上迭代而来,并没有重构这一部分,导致仍然存在代码赘余的情况。由于加入了三角函数化简的优化,导致代码行数有一定上升,总行数仍是一个相对合理的情况。

类复杂度分析

本次作业的复杂度相较上次在Expr与Function上复杂度增加了。Function类因为要实现嵌套自定义函数和求和函数,所以复杂度上升了。Expr中为了实现三角函数的化简导致复杂度上升。即便如此,整体复杂度仍处于一个合理的水平。

5、优缺点分析

优点分析

本次作业对上次作业存在的一些问题进行了重构与修复,并且相较上次进行了三角函数的平方和、sin二倍角和cos二倍角的化简,使本次化简更为彻底,性能表现更出色。

缺点分析

本次作业仍存在一定问题。为了方便,在修改sin类与cos类的时候没有吸取上次作业的教训用继承方法实现,这导致调试时如果sin写错了必须在cos类中对应的方法进行修复,增大了调试的难度。并且,在实现三角函数化简时写错了一行代码产生了一个很底层的bug,虽然侥幸通过强测,但在互测中损失惨重。

6、优化与Bug总结

优化

本次作业在上次作业的基础上增添了三角函数相关的化简

sin(-) -> -sin()
cos(-) -> cos()
sin()**2 + cos()**2 -> 1
2**n * sin()**n * cos()**n -> sin(2**n * )
cos()**2 - sin()**2 -> cos(2 * )

Bug

本次作业在强测环节没有被找到bug,但在互测环节中被找到了3个bug,分别是三角函数提取负号时错用了次幂导致问题,求和函数上下界没有考虑bigint,求和函数没有处理i**2的情形。本次bug相对较多,其中sum的bug 是第二次作业遗留的,因为上次作业中互测环节没有允许使用sum函数,所以没有对sum进行充分的测试就放在本次实验中沿用了,导致了两个bug产生,而三角函数化简哪里的bug也是因为测试不充分导致的,下次应当吸取教训,丰富测试数据集。

心得体会

1、架构设计体会

本单元作业分三次层层递进迭代开发而成,在开发过程中我深深体会到了好的架构对于进一步迭代开发的帮助。在第一次作业时我就确立了以递归下降法解析表达式,通过设立monoexpr作为最小计算单元来实现计算与化简的架构。这个架构一直沿用到第三次作业,并且没有做大的改动,说明可扩展性不错,开发的过程也比较顺利。第二次作业相对于第一次作业在解析字符串之前做了function和sum的字符串替换预处理,并在monoexpr中加入了sin容器和cos容器。第三次作业则扩展了第二次作业的限制,修复了一些不足。其中,第二次作业实现三角函数时没有考虑到进一步的扩展可能,导致第三次作业对这部分进行了大的改动,这也是没有考虑到可扩展性的开发所导致的问题,如果第二次作业就留下了相应的空间,第三次作业的任务量就会更轻了,能够测试项目的时间也就更充裕,可能最后的bug也就i能测试出来及时修复了。

2、测试与bug的体会

面向对象的作业相较于其他课程的作业最大的一点不同就是引入了互测环节,从而训练我们对程序的测试能力。本次作业仍是单线程程序,所以能够采用黑盒测试。在第一次作业时甚至采用了递归下降生成测试数据来对拍的测试方法,因为后两次作业没能写出构造数据的程序,导致自己的bug也没能发现。对于bug的产生,除了最基础的写错代码之外,最容易犯的还是没留意输入数据限制导致的bug,比如数据溢出,或者测试数据中产生了无法解析的格式。所以可以通过通读数据限制后构造边界数据来进行测试。除了黑盒测试之外,还可以通过通读代码来寻找bug,在本单元的训练过程中更多关注的是是否在优化环节与解析环节出错,也有可能找到对应的bug。

3、个人总结

本次作业相当有挑战性,也是我第一次接触到面向对象的编程过程。本单元的作业训练了我的抽象能力和面向对象思想,可扩展意识与迭代开发能力,编写维护一定规模项目的能力。作业也使我快速入门了java语言,能够运用java语言写出一定规模的项目了。

posted @ 2022-03-26 15:09  silhouette-  阅读(33)  评论(0编辑  收藏  举报