OO第一单元总结
第一次作业
结构分析
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
MainClass.main(String[]) | 1 | 1 | 1 |
Poly.Derive() | 1 | 3 | 3 |
Poly.Output() | 1 | 6 | 9 |
Poly.Poly() | 1 | 1 | 1 |
Poly.Poly(String) | 1 | 2 | 3 |
Poly.add(BigDecimal,BigDecimal) | 2 | 2 | 3 |
Poly.add(String,int) | 1 | 6 | 8 |
Class | OCavg | WMC |
---|---|---|
MainClass | 1 | 1 |
Poly | 4.17 | 25 |
第一次作业我的设计比较简单,用正则表达式解析输入,用Poly类来保存多项式,其中用HashMap保存每一项的系数和指数。这样的架构好写,但是缺点也致命,那就是缺乏可扩展性,导致我第二次作业需要重构。
Bug分析
由于第一次作业内容简单、代码量小,我在强测、互测中都没有被测出bug
发现别人Bug所采用的策略
我没有手写评测机做黑箱测试,而是通过看别人代码的方式做白箱测试,但是构造了一些我构思的可能出错的点,一无所获。最终我们房间也没有一起有效Hack。
第二次作业
结构分析
Poly.add(Poly) | 1 | 1 | 1 |
---|---|---|---|
Poly.add(Term) | 1 | 1 | 1 |
Poly.clone() | 1 | 2 | 2 |
Poly.insert(Poly) | 1 | 2 | 2 |
Poly.insert(Term) | 4 | 4 | 5 |
Poly.isEmpty() | 1 | 1 | 1 |
Poly.toString() | 1 | 4 | 6 |
PowerFunc.PowerFunc(BigInteger) | 1 | 1 | 1 |
PowerFunc.clone() | 1 | 1 | 1 |
PowerFunc.derive() | 1 | 2 | 2 |
PowerFunc.equals(Object) | 2 | 1 | 2 |
PowerFunc.getType() | 1 | 1 | 1 |
PowerFunc.hashCode() | 1 | 1 | 1 |
PowerFunc.multiply(Factor) | 1 | 2 | 2 |
PowerFunc.toString() | 3 | 1 | 3 |
SinFunc.SinFunc(BigInteger) | 1 | 1 | 1 |
SinFunc.clone() | 1 | 1 | 1 |
SinFunc.derive() | 1 | 2 | 2 |
SinFunc.equals(Object) | 2 | 1 | 2 |
SinFunc.getType() | 1 | 1 | 1 |
SinFunc.hashCode() | 1 | 1 | 1 |
SinFunc.multiply(Factor) | 1 | 2 | 2 |
SinFunc.toString() | 2 | 1 | 2 |
Term.clone() | 1 | 2 | 2 |
Term.derive() | 5 | 4 | 5 |
Term.getSign() | 2 | 1 | 2 |
Term.insert(Factor) | 1 | 2 | 2 |
Term.insert(Term) | 1 | 2 | 2 |
Term.mergable(Term) | 7 | 5 | 9 |
Term.merge(Term) | 1 | 3 | 3 |
Term.multply(Factor) | 1 | 1 | 1 |
Term.multply(Term) | 1 | 1 | 1 |
Term.toString() | 3 | 7 | 9 |
Class | OCavg | WMC |
---|---|---|
ConstantFactor | 1.2 | 12 |
CosFunc | 1.5 | 12 |
MainClass | 4.36 | 61 |
Poly | 2.22 | 20 |
PowerFunc | 1.62 | 13 |
SinFunc | 1.5 | 12 |
Term | 3.2 | 32 |
第二次作业我重构了我的设计,换成一种使用类似于自动机(这篇中的讨论是以我第三次作业的架构写的,不过差别不大)的方式解析输入并判断格式。而保存的方式为Poly-Term-Factor三级,Poly中保存由Term组成的Arraylist,Term中保存由<Type,Factor>组成的HashMap,而Factor作为接口实现了ConstantFactor,PowerFunction,SinFunction,CosFunction四种类。
求导也是由各级之间分工完成。Poly对其中的每个Term逐个调用其求导方法,而Term中逐因子调用求导方法,再由各个Factor返回求导的结果。
Bug分析
这次作业我出现了一个bug,这个bug是当输出系数为0的项的时候没有输出加号(由于自己时间不够了,就没有把输出优化做到位)。这个bug的产生和我的架构无关,但是和我的开发、测试方式有很大关系。我沿用了OI时代养成的一个坏习惯,总是喜欢写完所有代码后才开始一个一个debug,而不是写完一个模块就测试一个模块,导致我debug时面对着800+行代码无法一一发现其中的细节错误。这个bug给我的教训就是要写完一个模块就测试一个模块,不要等把所有代码写完后一起测试。
另外我的架构有个不好的地方是把对输入的处理写在了主类中,而没有另写一个类,这导致我的输入处理写的偏面向过程。
发现别人Bug所采用的策略
这次我依然是自己预先构思一些测试数据与阅读别人代码相结合,遗憾的是尽管我们房间有一些有效Hack,我依然没有找到别人的bug,并且代码量的增加也导致了阅读代码的测试方法的效率大大降低。
应用对象创建模式
这次作业中,我的输入处理会在解析出因子的类别、指数等信息以后交给对应的类来创建对象,然后加入包含这个因子的项中,再把创建好的项加入表达式。
对比和心得体会
我的测试方法需要改进,在OI中代码量一般只有100行不到,多的也很少超过200行,把代码全写好再整体测试的方法还算可行。但是在几百行上千行的工程中,这样的测试方法必然是无法把每个细枝末节都测试到,因此也就很容易有漏网之bug。
第三次作业
结构分析
Method | ev(G) | iv(G) | v(G) |
---|---|---|---|
ConstantFactor.ConstantFactor(BigInteger) | 1 | 1 | 1 |
ConstantFactor.clone() | 1 | 1 | 1 |
ConstantFactor.derive() | 1 | 1 | 1 |
ConstantFactor.equals(Object) | 2 | 1 | 2 |
ConstantFactor.getConstant() | 1 | 1 | 1 |
ConstantFactor.getType() | 1 | 1 | 1 |
ConstantFactor.hashCode() | 1 | 1 | 1 |
ConstantFactor.isOne() | 1 | 1 | 1 |
ConstantFactor.multiply(Factor) | 1 | 2 | 2 |
ConstantFactor.setConstant(BigInteger) | 1 | 1 | 1 |
ConstantFactor.toString() | 1 | 1 | 1 |
CosFunc.CosFunc(BigInteger,Factor) | 1 | 1 | 1 |
CosFunc.clone() | 1 | 1 | 1 |
CosFunc.derive() | 1 | 2 | 2 |
CosFunc.equals(Object) | 2 | 2 | 3 |
CosFunc.getType() | 1 | 1 | 1 |
CosFunc.hashCode() | 1 | 1 | 1 |
CosFunc.isOne() | 1 | 2 | 2 |
CosFunc.multiply(Factor) | 1 | 2 | 2 |
CosFunc.toString() | 2 | 1 | 2 |
MainClass.constantProcess(String) | 3 | 4 | 8 |
MainClass.cosProcess(String) | 4 | 2 | 5 |
MainClass.factorProcess(String) | 8 | 8 | 9 |
MainClass.getFactor(String) | 3 | 4 | 7 |
MainClass.getPoly(String) | 8 | 5 | 11 |
MainClass.getPower(String) | 5 | 4 | 9 |
MainClass.isNum(char) | 1 | 1 | 2 |
MainClass.isSign(char) | 1 | 1 | 2 |
MainClass.main(String[]) | 1 | 5 | 5 |
MainClass.passWhite(String) | 1 | 2 | 3 |
MainClass.polyProcess(String) | 4 | 2 | 5 |
MainClass.powerProcess(String) | 2 | 1 | 2 |
MainClass.sinProcess(String) | 4 | 2 | 5 |
MainClass.termProcess(String,int) | 9 | 5 | 11 |
MainClass.white(char) | 1 | 1 | 2 |
Poly.Derive() | 1 | 2 | 2 |
Poly.Poly() | 1 | 1 | 1 |
Poly.add(Poly) | 1 | 1 | 1 |
Poly.add(Term) | 1 | 1 | 1 |
Poly.clone() | 1 | 2 | 2 |
Poly.derive() | 1 | 2 | 2 |
Poly.equals(Object) | 2 | 1 | 2 |
Poly.getPower() | 1 | 1 | 1 |
Poly.getType() | 1 | 1 | 1 |
Poly.hashCode() | 1 | 1 | 1 |
Poly.insert(Poly) | 1 | 2 | 2 |
Poly.insert(Term) | 5 | 5 | 6 |
Poly.isEmpty() | 1 | 1 | 1 |
Poly.isOne() | 1 | 1 | 1 |
Poly.multiply(Factor) | 1 | 2 | 2 |
Poly.toFactor() | 3 | 1 | 3 |
Poly.toString() | 1 | 6 | 8 |
Poly.toTerm() | 3 | 3 | 4 |
PowerFunc.PowerFunc(BigInteger) | 1 | 1 | 1 |
PowerFunc.clone() | 1 | 1 | 1 |
PowerFunc.derive() | 1 | 2 | 2 |
PowerFunc.equals(Object) | 2 | 1 | 2 |
PowerFunc.getType() | 1 | 1 | 1 |
PowerFunc.hashCode() | 1 | 1 | 1 |
PowerFunc.isOne() | 1 | 1 | 1 |
PowerFunc.multiply(Factor) | 1 | 2 | 2 |
PowerFunc.toString() | 2 | 1 | 2 |
SinFunc.SinFunc(BigInteger,Factor) | 1 | 1 | 1 |
SinFunc.clone() | 1 | 1 | 1 |
SinFunc.derive() | 1 | 2 | 2 |
SinFunc.equals(Object) | 2 | 2 | 3 |
SinFunc.getType() | 1 | 1 | 1 |
SinFunc.hashCode() | 1 | 1 | 1 |
SinFunc.isOne() | 1 | 1 | 1 |
SinFunc.isZero() | 1 | 1 | 1 |
SinFunc.multiply(Factor) | 1 | 2 | 2 |
SinFunc.toString() | 2 | 1 | 2 |
Term.clone() | 1 | 2 | 2 |
Term.derive() | 5 | 4 | 5 |
Term.getSign() | 2 | 1 | 2 |
Term.insert(Factor) | 6 | 7 | 8 |
Term.insert(Term) | 1 | 2 | 2 |
Term.mergable(Term) | 7 | 5 | 9 |
Term.merge(Term) | 1 | 3 | 3 |
Term.multply(Factor) | 1 | 1 | 1 |
Term.multply(Term) | 1 | 1 | 1 |
Term.toFactor() | 4 | 2 | 4 |
Term.toPoly() | 5 | 2 | 5 |
Term.toString() | 3 | 7 | 10 |
ConstantFactor | 1.18 | 13 |
---|---|---|
CosFunc | 1.44 | 13 |
MainClass | 4.4 | 66 |
Poly | 2.17 | 39 |
PowerFunc | 1.44 | 13 |
SinFunc | 1.4 | 14 |
Term | 4 | 48 |
第三次作业比起第二次作业多了嵌套的情况,对于这一点我虽然没有在第二次作业中直接支持,但是也预留了扩展的余地。比起第二次作业开天辟地,第三次作业我只做了小小的改动便满足了需求。但是我的架构也有不少不好的地方。
首先输入的处理依然在主类中,依然有很明显的面向过程的影子。并且这次为了简化输出,给Term类添加了更多优化方法,导致Term类的方法数过多。另外,由于支持表达式嵌套,PolyProcess方法中出现了间接自己调用自己的递归,整个存储结构则出现了Poly-Term-Factor-Poly这样的相互调用。
Bug分析
本次作业我又出现了一个bug,由于沿用了上次作业中把x**2表达为x*x的优化,结果输出结果出现了格式错误。这虽然和架构无关,但是依然是我的考虑不周造成的。
发现别人Bug所采用的策略
本次互测,我的测试手段进行了大幅提升,变成了以手写评测机黑盒测试为主,手动针对性构造为辅。大多数人上次使用正则表达式解析输入,在本次作业中不得不重构,结果就有不少人栽在了输入上,有的人对于正负号的处理出现了错误,有的人把正确格式判成WF。
应用对象创建模式来重构
对象的创建基本和上次作业一样
对比和心得体会
两次作业我都因为一个细节的错误而大量失分,心痛之余,也不得不反思自己的坏习惯到底有多可怕。我应该多看看讨论区中关于测试方法等等的讨论。看别人成形的经验,了解大工程的开发、测试方法,这些是很有必要的,我会发现困扰我的问题其实别人早已提出了成熟的解决方案,比如TDD、Junit。我一直对讨论区中分享的方法有一种误解,以为不过是些花拳绣腿,对于我这样的实用主义者性价比太低。但是事实证明了,搞这些东西的人他们也是实用主义者,他们做这些就是为了避免更大的损失,这些损失,轻则是作业中无谓的丢分,重则可能是工作中项目的不可维护乃至饭碗的破碎。前人走过的路、踩过的坑,我更应吸取他们的教训,领会他们的经验,而不是以盲目的自信四处乱闯,结果重蹈覆辙。