OO第一次博客
OO第一单元总结
第一次作业
作业设计结构
本次作业结构不算复杂,因此我只构建了5个类来处理本次问题。Main类作为总控制类,它依次调用其他四个类。Polynomial类用于检测输入串是否合法并返回包含分割完成的多项式的每一项的字符串数组。Item类则用于将每一项化为变量x的指数和系数并存入Hashmap中,Derivation类用于从Hashmap中提取每一项后求导并将结果存入另一个Hashmap中返回,Print类则用于将结果简化后输出,简化主要在系数为0,1,-1、指数为0,1,以及第一项是否为正这几种情况处理。
第一次作业的类图
作业度量分析
本次作业中我的Item类中的Item构造方法以及Print类中的printFirst和printElse方法在设计上出现了漏洞,使用了大量的if else语句,建立了太多分支使得代码的模块耦合度过高,易于出现BUG,同时难以修复。
第一次作业的度量图
作业BUG分析
本次作业我的程序没有被找到BUG。
发现他人BUG策略
由于本次作业代码量小,代码结构性强,因此我主要选择在分析他人代码结构之后分别阅读他人源码,仔细分析他人可能存在的漏洞,并根据漏洞来进行hack。最终成功hack他人4次,得到2.1分。
第二次作业
作业设计结构
本次作业结构比第一次要复杂不少,但是幸运的是我们依旧可以找到一个通式来描述每一项,因此我此次构建了6个类来处理本次问题。Main类仍然作为总控制类,它依次调用其他四个除Key以外的类。
Polynomial类依旧用于检测输入串是否合法并返回包含分割完成的多项式的每一项的字符串数组。新加入的Key类用于描述项的通式,即x的指数、sin(x)的指数和cos(x)的指数,并重写了该类的hashcode、equals方法。Item类则用于将每一项化为项的系数、x的指数、sin(x)的指数和cos(x)的指数并将其存为Key与value并存入Hashmap中,Derivation类按照新的求导方式从Hashmap中提取每一项后求导并将结果存入另一个Hashmap中返回,并进行结果的三角函数的简化,Print类则用于将结果简化后输出。
本次的简化比起上次作业来说复杂了很多倍,我的简化除了对系数为0,1,-1、指数为0,1,以及第一项是否为正这几种情况处理以外还有对三角函数的简化,主要在于将a*x^m*sin(x)^(t+2)*cos(x)^t+b*x^m*sin(x)^t*cos(x)^(t+2)进行化简,将两项中系数较大的分为两项,一个是较小的项的系数,一个是系数之差的绝对值,之后利用sin(x)^2+cos(x)^2=1进行化简,另一种化简方式是利用1-sin(x)^2=cos(x)^2和1-cos(x)^2=sin(x)^2来化简a*x^m*sin(x)^n*cos(x)^t-a*x^m*sin(x)^n*cos(x)^(t+2)类型的式子。我对这种方法的处理结果并不满意,因为这种处理方式带来的优化效果不明显,更好的方式是大佬们在交流课上所讲解的利用Treemap以及随机算法来实现的优化。
第二次作业的类图
作业度量分析
本次作业中我的不少方法都在设计上出现了漏洞,比如Item的构造方法,它的大部分代码与我之前的Item相同,使用了大量的if else语句,同时我为了实现它新增的需求增加了更多的if else语句,又如我在Derivation类中新编写的simplifyAndGet方法为了判断式子能否优化使用了三层嵌套循环,大量的分支语句。这些方法与第一次作业的问题一样,建立了太多分支使得代码的模块耦合度过高,易于出现BUG,同时难以修复。
第二次作业的度量图
作业BUG分析
因为第二次作业的优化较为繁琐因此我的程序在优化过程中出现了BUG,主要在于在双重嵌套循环寻找可以合并的项的过程中在第二层循环中没有判断外层循环的项是否已经被合并,导致运行结果多项;没有增加合并后的项系数为零时不加入Hashmap,导致在print执行中既没有判断Hashmap为空,又因系数为零省略输出导致没有输出的现象。
for (int i = 0; i < number; i++) { if (book[i] == 1) { continue; } for (int j = i + 1; j < number; j++) { if (book[i] == 1 || book[j] == 1) {//bug after fixed continue; } ...... } }
if (ans.containsKey(tmp)) {
ans.put(tmp, num1.add(ans.get(tmp)));
if (ans.get(tmp).compareTo(BigInteger.ZERO) == 0) {//bug after fixed
ans.remove(tmp);
}
}
else if (num1.compareTo(BigInteger.ZERO) != 0) {//bug after fixed
ans.put(tmp, num1);
}
发现他人BUG策略
由于本次作业代码量明显增大,因此我选择既利用自己编写的评测机查询其对通过正则表达式生成的数据以及通过随机数生成的数据的处理结果得出BUG,同时又使用自己在编写代码时曾忽视的易错点来分析对方程序,编写数据,进行hack,成功率较高,甚至找到了一个其余人都没有成功找到的BUG,得到了5.5分。
第三次作业
作业设计结构
本次作业结构相当复杂,因此我构建了11个类来处理本次问题。Main类此次并不作为总控制类,它仅仅调用了Format、Plus类,前者将分析输入格式,并将输入转化为Plus类型的树状结构,后者则担负求导、化简并输出的任务。
Jobs与Abstract其实有些重复,我应该将Jobs接口完全删除而改为Abstract抽象类。这两个类规定了以下七个类的一些方法,SignedNum(带符号整数),PowFun(幂函数),SinFun(正弦函数)吗,CosFun(余弦函数),Nesting(带嵌套的三角函数),Multi(乘积形式),Plus(加和形式)。Plus内部只包含Multi类型的数据,Multi内部包含其余六种类型的数据。
程序运行时Format类利用括号匹配得到各层表达式因子,通过递归进行判断格式,分解各因子,化为各种类型,最终形成一个以Plus类型的数据为根节点的树。之后通过调用Plus等类中的derivation方法得到求导后的Plus类型的数据。在求导过程中,我发现了Multi可以容纳无限多个数据的属性会给求导带来不小的影响,我需要通过构建另外两个Multi类进行递归才能求导,因此我认为可能将Multi设置为可以容纳两个所有类型的数据更加合理。最终使用Plus等类中的print方法得到可以输出的字符串。在print方法中我采用了去除一些易于检查的括号、合并了带符号整数、幂函数、三角函数的同类项、对系数为0、1、-1和指数为0、1的数据输出进行了化简。
得到输出字符串后我利用了我在读入时会合并同类项的特性,循环进行读入字符串、输出字符串,直至得到的字符串长度不再减少。
第三次作业的类图
作业度量分析
本次作业中我由于代码风格问题,很多代码写的不够漂亮,Format类、Multi类和Plus类都写得很长,循环和条件语句多,WMC爆炸,在Multi类的simplify方法中几乎全部的语句都在分支语句中,在Format的polyReplace方法中,我在大循环中使用if语句进行了大量特判,还进行了递归操作,这使得我ev(G)、iv(G)、v(G)都相当之高。
第三次作业的度量图
作业BUG分析
本次作业我的程序没有被找到BUG。
发现他人BUG策略
由于本次作业代码量相当大,因此我选择按照第二次作业的思路,主要依赖于自己写的评测机,由于当时一些其他事情的影响,我只是额外检查了一些我在刚开始编写代码时没有想到要处理的一些情况。最终我成功hack了三次,得到了1.92分。
Applying Creational Pattern
第一次、第二次作业我基本上并没有写出一个面向对象的程序,我觉得我可以通过借鉴第三次作业的思路,将一个式子看作一个类来存储式子中的数据、设定式子的构造方法、求导方法、化简方法、输出方法。
第三次作业我认为我应当将Jobs接口合并到Abstract抽象类中,给Abstract加入simplify和print的抽象方法,将Multi类改为可以容纳两个所有类型的数据,更改它的各类方法,来让这一程序更加面向对象。