OO第一单元总结
第一次作业
需求分析
第一次作业的要求是多项式求导,其中项就是常数与幂函数的乘积,项与项之间就是普通的加减,并且并无格式检查(即输进来的数据都是符合要求的)。
设计思路
我只设计了一个poly类和main类来解决问题。其中main类承担了标准化输入(即把空格删一删,连续的+-号替换)、分析格式并提取关键数(是靠正则表达式来进行匹配并提取)来创建poly对象的引用并将其添加到list容器中、遍历容器再用treeset存储求导后的项以实现合并同类项(用treeset是因为我一开始像按照幂次高低输出,后来发现这并不能简化输出,所以这里其实完全可以换成hashset,合并同类项重写存储poly的treeset的compareto方法实现)和简单的优化输出(尽量将正项放前面和删去不必要的+号)的功能。poly就是存储一项中系数和幂指数的类,并且提供求导方法。其中tostring方法实现了输出的部分优化。
UML类图

程序复杂度

反思
可以看出我的main类复杂度有点高,原因是main类承担了较多的功能,像标准化输入,poly对象引用的创建以及最后对输出的进一步优化。这就使得main比较臃肿,直到第三次作业我都没有意识到这点,今日写总结时才感受到。事实上我应该把这些功能用其他模块来实现,而非堆在一起。另外poly中常数因子与幂函数因子混在一起,显然是不具有良好的扩展性的(添加新的因子就需要重构了),这些都是在第一次作业中出现的问题。
bug分析
第一次作业我并没有bug,但由于对treeset的理解不深导致在合并同类项时出了一点问题,所以性能分有所影响。我测试的时候是采用手撸数据的方法(我不太会写评测机),成功hack到了别人,构造测试用例的方法就主要在-1,0,1这三个数上做文章,因为优化不好可能会导致出错。
第二次作业
需求分析
这次作业在第一次作业的基础上增加了简单正余弦函数的求导,同时增加了判断合法性功能。
设计思路
显然第一次的思路是无法延续的,所以我进行了重构。
main类进行合法性判断(这里不敢用大正则所以选择了分割的方式,分割成项再判断项的合法性),标准化输入以及将表达式分割为项并创建term类对象的引用,同时完成部分输出的优化。term即项类,他完成对项的分析即分割成各个因子并创建不同因子对象的引用,这里是写了factor类并且让Constant类、Cosfactor类、Power类、Sinfactor类都继承自factor类,实现求导方法的重写(但是这里factor事实上应该是抽象类,因为它没有什么属性,只是有一些方法,但我当时写的时候理解不深,就写成普通的类了)因为每一项可以由常数、幂函数指数、三角函数指数来代表,所以term将每一项都化作了四元组,即四种因子的组合。同时term实现了对项的求导(我这里只把它当作4个因子相乘的项来求导,但第三次作业一个项可以有许多不同的因子,所以这里其实利用的是乘法求导公式写循环才利于扩展,这就为第三次重写埋下了伏笔),termder类则是为了方便进行求导后的合并同类项而创建的类,在main中用hashset存储,重写了hashset的hashcode和equal算法已合并求导后的同类项。最后在main中我又对满足sin(x)^2+cos(x)^2=1的项进行合并(用循环暴力搜索),当然这里的思考不是很全面,像f(x)-f(x)cos(x)^2这种就是我没有考虑到的,所以性能分丢掉了一些。Regex类主要是存储固定的正则表达式方便使用。
UML类图

程序复杂度

反思
与上一次一样,main复杂度过高,原因也是与上一次一样,实现功能太多,包括合法性判断,输入的预处理,输出的部分优化等等都在main类里面,在此不再赘述。
bug分析
这次我的程序在互测中被别人hack到一个bug(强侧没测出来也是很诡异,因为这个bug还挺严重的),就是在输入预处理,将相连的+-号替换掉时,由于我的疏忽,在正则表达式中“|”的前后是不同的,他会优先匹配前面的,所以在写的时候应该先把3个符号相连的写在前面,但是我却把2个符号相连的写到了前面导致不能正确替换3个符号相连的情况(其实我是知道这一点的,但当时写的时候却没注意到)。
在本次互测中我并没有hack到别人,主要是构造的样例有点普通,没有挑出易错点,在第三次作业中我实现了改进。
第三次作业
需求分析
在第二次作业的基础上。加入括号使得出现了多种嵌套组合方式,并且加强了对合法性的考察。
设计思路
说实话这次我设计只是为了实现功能而设计,在一些部分我没有再把因子给分出去而是直接整合到了term类即项类中,主要是为了将对项的求导整合到一起,现在再想想这样其实不太好,还是老老实实的把项的求导与因子的求导分开来比较好。这次与第二次类似,main类依旧实现了合法性检查,以及标准化输入的功能,poly类实现了对对表达式的求导功能(其实就是把表达式分割成一个一个项,然后利用项来求导),term实现了对项的求导,term中其实就是将项分成因子来进行求导,利用乘法原则来完成对项的求导,其中表达式因子比较特殊在括号中,整体是一个递归的思路来解决括号嵌套的问题,逐层括号返回求导结果再实现整合即可,现在看来把这么多内容全放在term里也不知道当时我是怎么想的。Regex与上次一样,存储常用的正则表达式。优化方面这次出现括号我怕出错就没有尝试过多的优化。
UML类图

程序复杂度

反思
毫无疑问这次写的不太好,3个类复杂度全部爆红,这也给了我一个教训,写代码要一步一步来,要有层次而不是像我这样堆在一起不便于之后的扩展和维护,课上讲的工厂模式也没有怎么用,就纯粹怎么想怎么来了,这些都是我在以后需要改进的地方。以后写代码要在写之前就有个良好的思路布局,包括各个结构模式的使用,这样不仅仅写的过程舒服很多,debug也会方便很多。这次没怎么化简确实遗憾,听助教说这样太怂不是件好事,想了想也很有道理,应该把目光放在如何写好程序,如何更加完美的实现要求上,而不是去追求那所谓的正确性。
bug分析
这次程序更加体现出我这个人粗枝大叶的毛病了,在两个细节上都出了bug,分别是去除空白字符时,我是挑选出不符合要求的空白字符然后就输出wf,结构由于是边写边想,以至于漏掉了一个情况,在符号和整数之间不能有空格,我在判断这个时忘记了当表达式在括号中时的情况,所以少判断了一个wf,其实只要在正则表达式中加一个“(”即可。另一个细节就很蠢,我居然把**1给直接去掉了,所以比如当出现**12时我就会变成只有2了,本来是要判断指数为一可以直接省略的,结果直接在输出的结果中判断偷鸡不成蚀把米,也是一个教训吧,写程序老老实实才不会错,想当然是不可取的。互测时我认真构造了一些样例除常规测试还涉及到多层嵌套测试、特殊空格测试、sin(0)**0之类特殊测试,也成功hack了12次,看样子和我一起的也都是马大哈了,也希望经过这次总结我们都可以进步,减小犯错误的概率。很多东西最好动手写一写,不能光在脑子里想,光想容易想不周到,不全面,我也要完善自己的思维模式。
综合反思
测试方面:
手撸数据的确容易寻找到特殊的数据发现bug,但这要求自己对易错要充分熟悉才行,由于自己的问题,我的代码评测基本就是指导书的样例和中测的样例(现在想想这是不太行的,很容易就会出现bug漏过去)。测试别人的时候我会阅读部分代码,有时会发现一些漏洞。主要还是靠精心构造特殊样例来测试,并且我希望自己也可以学习一下如何构造评测机(如果时间充裕的话),这样也可以减少人力的开支。
创建模式方面
自己可能只是对普通的继承,接口,多态有一定的理解,一开始由于不会反射机制导致我使用起来总是不顺手,明明是是要归一化可我扔进来一个数据怎么才知道这是哪个子类呢。在听了研讨课上的讲解有了豁然开朗的感觉。这三次作业说实话我感觉很差,整个来说工厂模式由于不熟练和部分知识不了解就没怎么用,三次几乎都重构了代码。在此之后在写代码时整体的思路必须要在一开始定下来,摆脱想到哪写到哪的毛病(这样是做不了复杂性高的工程的,只能解决一些小的问题)。尽力提高代码的可扩展性,迭代开发(不过感觉自己现在的理解也并没有达到迭代设计的要求,这些还需要我多多向同学学习,走出思维误区,提高自己的编程水平)。
心得体会
第一单元就这样磕磕绊绊的过来了,也算是对面向对象设计有了懵懵懂懂的认识(可能我理解东西比较慢),深刻认识到了架构的必要性,意识到高内聚低耦合的优势。有的同学第一次作业就处理的极好然后架构可以一直用到第三次作业,我这样想到什么写什么的代价就是几乎都在重构,问题确实很大。对于其他的方面上面基本也都写到了,最后以”路漫漫其修远兮,我将上下而求索“作结,勉励自己,多向优秀的同学学习,改变自己。
浙公网安备 33010602011771号