BUAAOO Unit1博客作业
Unit1总结博客
BUAAOO课程 第一次博客作业
第一次作业
题目概述
第一次作业为简单多项式求导,因子只有幂函数因子和常数因子,表达式中可能包含空白字符,但不包含括号。
架构设计
由于只有幂函数因子和常数因子,而幂函数和常数都可以表示成 coe*x*exp 的形式,而因子相乘后仍是幂函数的形式,故我采用正则表达式来逐个匹配因子,延用课上实验的方法建立了一个Item类代表每一项,一个Poly类用TreeMap来存放这些Item并合并同类项,并通过一个toString()函数实现了求导到字符串化的过程,没有建立因子项,还建立了一个Input类来获取输入表达式并对其进行空白项替换、多个 +- 号替换以及 -x 和 +x 向 +1*x 和 -1*x 的替换,以及一个Main主类。类图如下:

显而易见,这样的架构导致了很致命的问题,功能固化、可扩展性极低,基本无法实现后续功能需求,导致第二次作业重写。
代码度量
此次作业代码中类的个数、类内属性个数、方法个数如上图(类图)所示。
方法复杂度分析

类复杂度分析

类代码行数分析

耦合度分析

其中复杂度较高的方法有4个:
- Poly类中的构造方法复杂度很高,这是由于我在设计架构时,选择将一个项内的因子都视为 coe*x**exp 的形式逐个读取并直接计算相乘后的结果,之后生成一个Item的对象保存在Map中,这一过程完全在Poly的构造方法中进行。
- Item中toString方法复杂度高,原因在于对toString后的结果集中进行了一些优化,这部分较复杂,圈复杂度很高,在设计过程中非常容易出错。
- Item中equals方法重写和Poly中getItem方法用了很多分支判断导致较高的圈复杂度。
测试与bug分析
这次作业并未出现bug,可能是这个架构唯一的好处吧,设计起来十分简单,优化也很容易实现😂。
互测策略
此次互测中,并未搭建评测机,只用了手动生成边缘数据的方法来进行互测,在互测中hack了一个bug,原因是在大数处理时未考虑全面,导致bug出现。
第二次作业
题目概述
本次作业在第一次作业的基础上增加了简单三角函数因子 $sin(x)$、$cos(x)$ 和表达式因子(即用阔号括起来的表达式),其中三角函数因子可以有幂指形式,需求仍为对表达式求导。
架构设计
由于第一次作业考虑得过于片面,扩展难度极大,只好进行重构。在这次作业中,我采用了讨论区和助教反复提过地递归下降方法来分析表达式,但是由于自己理解得也不是很透彻,做起来困难重重,逻辑也不算太清。在这次作业中我建立了Factor类,并建立了Factor的子类:ConstantFactor常数因子类、Trigonometry三角函数因子类、PowerFunction幂函数因子类、ExpressFactor表达式因子类,因子类内部实现自己的求导和toString()方法;以及FactorFactory工厂类用于创建因子,Item类用来表示项,Input类用来处理输入的表达式字符串,和主类Main类。这次作业没有做很好的优化,性能分不好。代码类图如下:

代码度量
方法复杂度分析


类复杂度分析

类代码行数分析

耦合度分析

从代码复杂度分析可以看到:
- 复杂度较高的方法有Item类和Trigonometry类的der()方法,这是由于我设计过程中考虑不周全造成的问题,我将求导函数的结果直接返回为一个String字符串,对其直接进行输出,所以又复杂的分支语句来进行优化,而且优化效果很差。经过研讨课上大佬们的分享,求导函数不应当直接返回为一个字符串;而是应当返回其对应的一个Factor(包括变量因子、常数因子、表达式因子),然后调用这些对象的toString()方法即可得到求导后的输出结果,这样不仅能简化逻辑降低复杂度,还能够减少方法之间的耦合度。
- 复杂度较高的方法还有PowerFunction的toString()方法,这两个函数圈复杂度很高的原因也出在简化输出上,导致分支语句特别繁重。
- ExpressFactor中的getFactor()方法复杂度偏高,可能是由于循环与判断嵌套导致。
从耦合度分析能够看出,类之间的耦合度不算太大,但是从复杂度可以看出,类内复杂度有点偏高,也是我逻辑不清晰的问题吧。
测试与bug分析
这次作业中我出现了一个TLE的bug,原因是递归调用次数太多。在对表达式(因子)求导后的字符串进行优化的过程中,在进行分支语句判断时反复调用项的求导函数,导致在大量表达式因子嵌套的样例中递归深度大大增加,递归次数大大增加,使得程序出现了TLE,被3位同学找出Bug,强测也不少失分。
互测策略
在这次作业中,我借鉴了第一次作业中ch大佬给出的评测机搭建思路,自己搭建了一个评测机,来进行普遍性正确性测试,同时通过手动构造边缘数据,对自己和房间内的其他同学的代码进行测试。同时还大致阅读了其他同学的代码,针对性地hack,找到了一个bug。
互测过程中找出了两位同学的bug,分别为提取因子时出现问题和优化时对符号的优化考虑不周导致丢失正确性,房间内还有其他同学也出现了与我相同的bug。
第三次作业
第三次作业较第二次作业增加了一个嵌套复合的三角函数因子,即在三角函数的括号中可以是 x 也可以是其他任意因子。还增加了表达式格式判断的需求。
架构设计
本次作业延用第二次作业的架构,并没有做太大修改就完成了任务,唯一修改很多的地方就是FactorFactory创建因子时的异常判断处理以及三角函数因子中增添了属性引起的求导、toString()和分析的变化。由于要进行格式检查,这次采用严格按照形式化表述和递归下降方法来解析表达式,在生成因子的过程中审查表达式格式。
但是由于上一次架构遗留下来的难以优化的问题,此次作业求导性能依然一般。类图如下:

代码度量
方法复杂度分析


类复杂度分析

类代码行数分析

耦合度分析

这次的代码中复杂度较前两次作业复杂度大幅提升,除上一次复杂度高的函数之外还有其他函数的复杂度大幅提升,主要集中在FactorFactory和ExpressFactor中,原因如下:
- 在FactorFactory类的createFactor()函数与格式审查高度耦合,导致有很多的判断语句。
- 在生成Trigonometry因子时,需要识别括号内因子,如果括号内为表达式因子则需要递归识别因子,增加了递归。
- 在ExpressFactor类中的getItem()和getFactor()方法复杂度很高,这是由于在这些方法中,我增加了格式审查在这两部分中,导致代码逻辑复杂,增加了圈复杂度。
测试与bug分析
在这次作业中未出现bug。这次作业的测试过程中,首先利用更改后的评测机以及上次作业中出bug的样例进行了正确性测试,手动构造了一些WF的表达式,尽量全面覆盖,不过很可能并未达到全面覆盖测试。
互测策略
在这次互测中,只是简单地跑了跑数据,由于要肝冯如杯,并未仔细阅读其他同学的代码,在这次互测中并未认真完成任务,下次一定!(不是
心得体会
在这三次作业中,我感觉自己能够逐步了解面向对象设计的设计思维,也体会到了一些面向对象设计的好处。面向对象设计能够让整个程序逻辑清晰,类与类之间界限分明关系明了。但是不得不说设计的过程中真的需要头脑风暴,否则就会写出一个混乱的代码,比如我的代码;在周五研讨课上大佬们的程序设计思路分享真的让我感觉到自己代码的缺陷,如果增加变量个数怎么办,自己的代码只能大幅修改来满足需求,甚至需要重构,所以再拿到题目时一定要好好设计架构,思考如何满足6大设计原则,考虑以后可能会增加的需求,降低耦合度,增加可扩展性。
在设计过程中有很多东西无从下手,考虑了一下原因,对于一些方法如递归下降、工厂模式不是很了解,而且对Java的一些机制认识得不够清晰。这部分需要多加学习多加尝试。后两次作业中都是利用ArrayList来实现数据存储,没有灵活应用Map来实现,给性能优化带来了难题。
在作业中,我有一些冗余的类、冗余的方法,这些都是设计过程中思虑不周之处,比如Factor父类只提供了方法,未提供方法实现,这是个很不好的写法,可以按照老师上课讲解的将数据抽象改为行为抽象,利用接口来对类进行统一管理调用。

浙公网安备 33010602011771号