BUAAOOUnitOne总结
作者:互联网
BUAAOOUnitOne总结
第一次作业
基于度量的程序结构分析
-
UML类图
-
程序架构及设计
-
程序架构:由于第一次作业仅为项的加减组合形式,所以这次作业本人自己认为并没采取面向对象的编程思想,更多的还是面向过程的思想,这也是本次作业的不足之处,没要考虑到程序的鲁棒性,导致第二次作业必须需要重构。本次作业本人共设置了四个类,其中MainClass仅有一个Mian方法,是整个程序的入口,ItemRegex类负责管理项的五种形式的(c / a*x**b / a*x / x**b / x)正则表达式,Item类存储项,Polynomial类负责解析项、求导。这样在提取项的时候,比较简洁明了。
-
-
代码规模
-
复杂度分析
-
根据Metric插件复杂度分析可知,本次作业大面积飘红,经过分析,在提取各类项的时候,由于分析项前面的符号,而导致的ev(G)较高。应该增加预处理类,在提取各项之前,简化相应的符号。另外提取各类项的时候,绝大部分代码重复,应该采取抽象的方法来避免代码的重复。而parseItem()方法,由于在方法内调用了各getItem()方法,导致耦合度增加,应该减少在类的方法内部调用该内的其他方法,增加内聚性。而toString()方法,由于将项根据系数排序和简化输出全杂糅在一个方法中,导致了toString的复杂度极高,这也是本次作业一个十分不好的设计。
关于Debug和互测
-
由于本次作业并不复杂,本人在强测和互测中均未出现bug。
-
发现别人程序bug所采用的策略:本人在互测环节成功hack2次,首先是用搭建的测评机进行功能测试,然后采取手动构造边界数据的方法。在手动构造边界数据之前,经分析,该同学由于简化部分求导为0结果,将求导为0的结果返回“”,但是忽略了最后结果为“”时,应该输出0的情况,便可以构造相应的数据进行hack。
关于对象创建模式
-
本次作业是通过正则表达式匹配字符串达到创建对象的目的,并未采取有关设计模式。
第二次作业
基于度量的程序结构分析
-
UML类图
-
程序架构及设计
-
程序架构:从第二次作业开始,开始运用面向对象的思想进行设计,对第一次作业进行了重构。用具有求导功能的Func接口对因子(常数因子、三角函数因子、幂因子)进行统一管理,根据行为抽象层次进行层次化设计。相应的引入工厂模式,统一管理因子对象的创建。
-
WF:本次作业增加了WF的判断,本人采取了从下到上构建项的正则表达式的方法进行判断。
-
预处理:本次作业,为了避免在提取项和因子时由于符号所产生的麻烦,将非连接处的'+'号都省略,确保项与项的连接仅为'+', 将"**"替换为'^',从而可以根据'+'分离各个项,根据'*'分离各个因子。
-
解析项:根据'+'分离各个项,根据'*'分离各个因子之后,为了能够归一化管理项,设计Simplifyer类用于合并同类项,返回通项,然后遍历保存储存通项的数组,调用通项的求导法则即可。
-
化简:由于时间不够加上对存储项的数据结构的理解问题,本次作业本人只实现了最后结果的合并同类项功能以及基本因子的化简。
-
-
代码规模
-
复杂度分析
由于采用面向对象的设计思想,相较于第一次作业有了很大程度的改进,但是缺点也显而易见,冗余的方法很多,比如,为了实现合并同类项,在Func接口实现了因子相乘和相加的方法,使得每一个因子都得实现这两个方法,实际上有些因子并不需要,后经过大佬的指点,发现可以使用抽象类来解决这种情况。
另外Expression类的parse()方法、GeneralTerm的toString()方法模块设计复杂度和圈复杂度依然严重超标,经过分析发现,本人在parse()方法里面多次调用了Simplifyer类的merge()方法来化简同类项用来生成通项,没有遵循专家模式,使得模块之间的耦合度迅速增加,以及GeneralTerm的toString()方法,大量单一if语句而非if-else导致了结构不完整,圈复杂度超标。由于调用了其他因子的toString()方法,增加了模块设计复杂度。
关于Debug和互测
-
本人在强测和互测阶段均未出现bug
-
在互测中,按照第一次作业的策略,hack成功一次。通过分析该同学的代码,发现该同学为了保证幂函数的指数不超过10000,用正则表达式提取时规定了指数只能为4位数或者10000,没有考虑到00001,指数前导0的情况,导致误判了WRONG FORMAT。
关于对象创建模式
-
本次作业采取了简单工厂模式对因子对象的创建进行统一的管理。重构说明:新建FuncFactory,以及接口Func(实现求导功能),三角因子、常数因子、幂因子、通项均实现求导功能,根据工厂类getFunc方法传入参数列表的不同,实例化不同的因子,但这些因子均被Func引用,这样便可以使用Func接口来实现统一管理。
第三次作业
基于度量的程序结构分析
-
UML类图
-
程序架构及设计
-
程序架构:由于第二次作业时考虑了程序的鲁棒性问题,本次作业的架构和第二次基本一致。本次作业相较于第二次作业增加了三角嵌套因子,和表达式因子。这也使得不能再用一个通项去表示一个项了。为了实现嵌套本人增加了NestFactor和ExprFactor类,并且实现了Func接口。NestFactor采用ArrayList<Func>来保存数据,ExprFactor采用ArrayList<ArrayList<Func>>来保存数据。采用归一化思想,用接口统一管理因子,通过遍历因子求导,不同的因子会自动调用不同的求导法则,调用toString方法返回求导结果,来实现链式求导。
-
表达式解析以及括号栈设计:由于引入了三角嵌套因子和表达式因子,单纯的使用正则表达式来解析项和因子,本人采取的方法是括号栈的方法。首先通过Pretreatment类的Standardize()方法进行标准化(前提:前进行WF判断,然后将非连接处的'+'号省略,"**"替换成'^等),然后创建一个括号栈(遇到'('入栈,遇到')'且栈顶为'('出栈),首先根据栈为空时记录的'+'位置解析出项,然后根据栈为空时记录的'*'位置解析出因子。对于解析出的因子,同样可根据括号栈的方法递归解析嵌套因子。
-
本次作业由于难度较大,且化简易出错,仅完成了基本因子的化简。
-
-
代码规模
-
复杂度分析
通过Metric插件复杂度分析,随之需求的不断增加,代码的复杂度也有所提升,飘红的方法数目达6个。
进一步分析源代码发现,主要是由于Parser类parse项和因子的时候,需要不断遍历字符串,且各种条件判断较为复杂,难以模块化和维护,是的基本复杂度ev(G)、v(G)较高。Polynomial类的getFactor()方法,主要是因为递归调用来提取因子,使得三项复杂度均较高。
另外在处理NestFactor求导时,在求导时进行了各种条件的判断,进行一定程度的简化,从而使得模块设计复杂度较高。
关于Debug和互测
-
本人在强测为出现bug,但是在互测时被发现了1个bug。
-
bug原因分析:本人在处理-(-1*(x))这种嵌套的表达式因子的时候,在toString()方法的时候,遗漏了最内层(x)与-1之间的'*',导致了在表达式因子求导时出错。
-
本人在互测阶段成功hack2次,本次构造数据主要是采用手动构建极端测试数据(类似((((((x))))))这种可能TLE的数据)的方法,通过分析该同学代码,主要是因为嵌套表达式因子-(x-(x))误判为WF出错,由于其判断WF方法过于复杂,故采取的黑盒测试的方法。
关于对象创建模式
-
本次作业在第二次作业的基础上,采用了递归解析、创建对象的方法。
对比和心得体会
-
对比
-
面向对象设计:通过学习优秀代码,本人对面向对象的设计有了更全面的了解,比如,我们在设计时,应该时刻记得用户需要什么,以及不需要什么,从而选择相应的关键字,并不是一个类里面所有方法都是public,得根据用户的需求来,用户需要的就是可见的,即public,其余的最好封装起来。
-
专家的思想:一个类承担的任务有限,且将功能相似的模块建立一个类,也就是所谓的专家,能够使代码更简洁易懂。
-
递归:递归的思想也能运用于对WRONG FORMAT!的判断之中。
-
优化:本人在三次作业过程中,性能分并不是很好,如何判断两项是否可以化简以及设置相应的熔断机制给了本人很大的启发。
-
-
心得体会
通过三周的面向对象设计训练,本人从最初的面向过程到现在懂得运用一些面向对象的方法,归一化、工厂模式等等。另外,还有一点想说的话:
-
善于利用讨论区
讨论区大部分帖子都很有价值,比如关于如何搭建测评机、如何使自己代码的鲁棒性更好、如何进行化简等等。并且一些优秀的同学都十分乐意分享,本人也已经养成了每天写码之前先看讨论区的习惯。
-
多学习大佬的代码
大佬们的代码通常能够做到十分简洁且易懂,这样不仅自己看的清爽并且十分不容易出bug,另外还有一些方法会让你惊呼“原来还能这样?”。本人都会在周六晚公测结束后阅读大佬的代码,每次读完都很有收获。
-
重视测试
测试也是十分重要的一个环节,如果不想在互测中被人hack,那么前期完备的测试是不可少的,当然测试不可能测试全,提升自己的测试能力也是不可或缺的一部分。
-
面向对象的思想
采用面向对象的思想,可以减少工作量,并且能使程序的可扩展性更好,第三次作业,感觉如果不采用面向对象的思想可能很难写出来。
-
标签:总结,BUAAOOUnitOne,复杂度,作业,因子,求导,方法,互测 来源: https://www.cnblogs.com/zhouh1999/p/12533941.html