博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OO第一次博客作业
阅读量:4624 次
发布时间:2019-06-09

本文共 11979 字,大约阅读时间需要 39 分钟。

OO第一次博客作业

前言

本次博客作业,本人将以三次作业为单元进行分析,再借着讲查bug的方式说说自己的搭评测姬的方式,最后总结一些面向对象相关的经验。

第一次作业

UML结构图

1615461-20190326181827591-2027383389.png

结构分析

第一次OO作业的功能需求相对简单,本人把整个构造、简化输出以及它们用到的一些private方法全部丢到Polynomial类中实现,同时为了不让文件数量太少显得尴尬我专门写了个求导类,用于求导功能的实现。由于第一次作业的业务逻辑以及优化逻辑相对简单,故这种“大锅菜”式的设计并没有带来什么困扰,反而写的很快(雾

耦合度分析

1615461-20190326181855541-541398196.png

1615461-20190326181900979-296203073.png

如前文所言,本次作业,我写的比较面向过程,整个parse的过程全部丢在polynomial的一两个方法中完成,使得方法以及类的复杂度过高。

这种设计在面对第一次这种较为简单的设计尚有生存的余地,当工程的复杂度持续走高的时候,仍然用这种方法基本上是自掘坟墓。

难点分析

第一次作业的主要难点在于正则表达式解析字符串,基本上用小正则不断分解字符串即可,大正则的朋友是真的狼。500行状态机的狼灭们,求求您们住手

BUG分析

本人的BUG

1615461-20190326181915555-314969013.png

第一次作业除了输入部分没啥坑点,所以很不巧,本人强测、互测都没被找出bug(摊手手

同屋的BUG(高能预警)

1615461-20190326181930466-844215441.png

1615461-20190326181935305-366762113.png

第一次作业的强测有一点点小尴尬,100分的门槛太低,导致一个屋内的水平差异有丶大,所以就出现了5锤3到4锤4的惨烈场面。

第一次作业中的BUG基本没有在计算上的问题,除了那些被不知道怎么混进来的,被捅了20多刀的朋友,问题主要在于输入处理上,比如先trim了一下再去判\v\f的情况,以及神奇的大正则爆栈。

这里要说个小插曲,在第一次讨论课分享后,本人居然被人找上门婊了,对方的理由是“你不觉得你们这种找别人\v\f的bug的行为很钻营么?

这位同学当时的意思(我的理解)是官方指导书说可以有“空白字符”,所以我们用\v\f是钻营的。

1615461-20190326181944432-1223242518.jpg

exm??????

首先,WF问题本来就是官方规定的BUG之一,第一次作业并没有取消这一hack方式,所以,hack这种数据完全合乎规矩。

第二,互测本身就包括测试程序的鲁棒性,自然要测试程序在处理各种玄学输入情况的表现,输入\f\v有何不合理?

第三,官方给的“空白字符”有着明确的定义,横向制表符和空格符,我们输入\v\f不属于此类。既然您喜欢先入为主,代入自己的主观理解,同时不去认真理解指导书的语义,那您被\v\f数据hack又岂是他人之过?

第四,水群里,讨论区里对于\v\f是否属于空白字符的讨论还少么?既然您喜欢自我封闭地处理自己不明确的定义,那么就请做好被hack的准备。

第五,本人对于那位同学的水平也稍有认识,我不认为 ta 会跟我分到一个组,所以,冤有头,债有主,你找我干嘛?

好了,平静一下心情,看看下一个作业。

第二次作业

UML结构图

1615461-20190326182004425-1569744614.png

结构分析

这次设计我用了常见的三级层次,最顶层为Poly,Poly由Term组成,Term由Factor组成。

这次的设计中,我仍然把Poly的构造与字符串检查与解析放在了Poly的构造函数中实现,同时把各种优化方法,是否包含Term的检查放在Poly类内部实现,使得这个类过于臃肿。

此外,这次的Term类内部也包含了各种计算方法,使得这个类有点臃肿。

耦合度分析

Method ev(G) iv(G) v(G)
CosFactor.CosFactor(BigInteger) 1 1 1
CosFactor.derivation() 2 2 2
CosFactor.toString() 3 3 3
Factor.Factor(FactorType,BigInteger) 1 1 1
Factor.getDegree() 1 1 1
Factor.getType() 1 1 1
FactorFactory.produce(FactorType,BigInteger) 5 2 5
FactorFactory.produce(FactorType,String) 2 2 2
Poly.Poly() 1 1 1
Poly.Poly(ArrayList) 1 2 2
Poly.Poly(String) 3 2 5
Poly.add(Poly) 1 4 4
Poly.betterSolution(ArrayList,Term,Term) 3 4 9
Poly.canCos2ToOneSubSin2(Term,Term) 1 3 5
Poly.canSin2AddCos2(Term,Term) 1 2 3
Poly.canSin2ToOneSubCos2(Term,Term) 1 3 5
Poly.cleanUp() 1 3 3
Poly.derivation() 1 2 2
Poly.getArrayList() 1 1 1
Poly.getTerm(TermKey) 1 1 1
Poly.hasTerm(Term) 1 1 1
Poly.lengthBetter(Poly,Poly) 1 1 1
Poly.main(String[]) 1 3 3
Poly.mergeSin2AddCos2(Term,Term) 1 4 4
Poly.mult(Poly) 1 3 3
Poly.optimization() 1 6 9
Poly.parsePoly(String,Pattern[]) 2 5 6
Poly.parseTerm(Pattern[],Matcher,BigInteger) 5 11 11
Poly.simplifyCos2ToOneSubSin2(ArrayList,Term) 1 1 1
Poly.simplifySin2AddCos2(ArrayList,Term,Term) 1 1 1
Poly.simplifySin2ToOneSubCos2(ArrayList,Term) 1 1 1
Poly.sub(Poly) 1 2 2
Poly.toString() 3 3 4
PowerFactor.PowerFactor(BigInteger) 1 1 1
PowerFactor.derivation() 3 3 3
PowerFactor.toString() 3 3 3
SinFactor.SinFactor(BigInteger) 1 1 1
SinFactor.derivation() 2 2 2
SinFactor.toString() 3 3 3
Term.Term() 1 1 1
Term.Term(ArrayList) 1 1 1
Term.Term(BigInteger) 1 1 1
Term.Term(BigInteger,ArrayList) 1 1 1
Term.TermKey.equals(Object) 4 1 6
Term.TermKey.getFactor(FactorType) 1 1 1
Term.TermKey.hashCode() 1 1 1
Term.add(Term) 2 2 2
Term.contains(FactorType) 4 3 4
Term.copy() 1 1 1
Term.derivate() 1 4 4
Term.getCoe() 1 1 1
Term.getDegree(FactorType) 2 2 2
Term.getFactor(FactorType) 4 4 4
Term.getTermKey() 1 1 1
Term.mult(BigInteger) 1 1 1
Term.mult(Factor) 1 1 1
Term.mult(Term) 1 1 1
Term.put(Factor) 3 3 3
Term.toString() 7 7 9
Term.upToPoly() 1 1 1
Class OCavg WMC
CosFactor 2 6
Factor 1 3
FactorFactory 3.5 7
FactorType n/a 0
Poly 2.92 76
PowerFactor 2.33 7
SinFactor 2 6
Term 2.17 39
Term.TermKey 2 6

可见,由于上文所述的设计结构上的欠考虑,这次的耦合度与复杂度是过高了。

主要体现在字符串解析,与各种Term,Poly的计算全部放在内部实现,所导致的臃肿。经过这次教训,本人在第三次作业对结构做出了一定的调整。

难点分析

这次一部分同学已经采用了多态的操作,这也就意味着,他们的Term内部很可能包含着几个Factor对象。

求导工作就是基于这几个对象的,这也就带来了一个问题,你的对象应该是可变的还是不可变的,你的求导是直接改变对象自身属性还是新建一个对象。

我的建议是对象不可变,任何运算都得到一个新的对象。这样可以有效防止自己时不时出现写着写着修改了一些共享的对象,从而导致一些自己一时看来匪夷所思的bug。

除了这个小难点以外就是优化了,本人数学不太好,所以这次涉及到各种数学公式的无聊的优化,我就随便搞了两三个就摸了。

BUG分析

自己的BUG

本人这次没被查出bug,但是后来发现自己的hashcode似乎写错了,但又好像没错,果然姿势水平不高,语言特性不熟悉。

此外这次强测有一些小插曲,导致了互测的神奇情况

1615461-20190326182022057-318671382.png

本次公测中,我成功反向hack了公测数据(雾)

同屋BUG

1615461-20190326182030121-2127824831.png

本次互测中我测出来的的BUG主要集中在输入处理部分,这次的正则表达式比上次难度提升了一些,但是并没有本质区别,测出的bug更像是对指导书理解有偏差。

计算的BUG主要在于合并同类项的时候在系数和指数的处理上出了些小BUG,以及在求导时忘了把那一项的系数乘上(雾

总而言之,这次的BUG中,WF的比率相对降低,计算、求导、优化的BUG更多。

第三次作业

UML结构图

1615461-20190326182046429-913111590.png

结构分析

本次作业我大体沿用了第二次作业的设计思路,多项式的结构分为三个层次 Poly,Term,Factor

稍有不同的几点,

  1. 本次的设计中,我把常数项由原来的作为Term的属性改变为Term的成员,这么做的原因有如下几点
    • 嵌套因子求导会产生大量的新的常数,原有的方式处理起来很不优雅。
    • 常数因子从数学上来看确实应该算是因子的一种,支持求导等操作。
    • Term求导后的结果是一堆Factor,Term本身就是一个actor的管理者身份,正好可以生成一个全新的Term。
  2. 把字符串解析,Term,Poly,Factor的计算提取出来,变成Parser类与Calculator类,降低单个类的复杂度。
  3. 使用了自定义的异常。

耦合度分析

Method ev(G) iv(G) v(G)
code.Calculator.add(Term,Term) 5 5 5
code.Calculator.mult(Factor,BigInteger) 1 1 1
code.Calculator.mult(Factor,Factor) 1 1 1
code.Calculator.mult(Poly,BigInteger) 1 2 2
code.Calculator.mult(Poly,Poly) 1 3 3
code.Calculator.mult(Poly,Term) 1 2 2
code.Calculator.mult(Term,BigInteger) 1 1 1
code.Calculator.mult(Term,Factor) 3 3 3
code.Calculator.mult(Term,Term) 1 1 1
code.ConstantFactor.ConstantFactor() 1 1 1
code.ConstantFactor.ConstantFactor(BigInteger) 1 1 1
code.ConstantFactor.canExtract(Factor) 5 3 6
code.ConstantFactor.canMerge(Factor) 2 1 2
code.ConstantFactor.differential() 1 1 1
code.ConstantFactor.equals(Object) 3 1 3
code.ConstantFactor.extract(Factor) 2 1 2
code.ConstantFactor.getValue() 1 1 1
code.ConstantFactor.merge(Factor) 1 1 1
code.ConstantFactor.optimization() 1 1 1
code.ConstantFactor.toString() 1 1 1
code.CosFactor.CosFactor(BigInteger) 1 1 1
code.CosFactor.CosFactor(BigInteger,Factor) 1 1 1
code.CosFactor.canExtract(Factor) 5 4 7
code.CosFactor.canMerge(Factor) 4 4 6
code.CosFactor.differential() 3 2 3
code.CosFactor.equals(Object) 5 4 7
code.CosFactor.extract(Factor) 1 2 2
code.CosFactor.getInnerFactor() 1 1 1
code.CosFactor.merge(Factor) 1 1 1
code.CosFactor.optimization() 2 2 3
code.CosFactor.toString() 4 4 4
code.Extracter.extractForPoly(Poly) 1 8 9
code.Extracter.extractTerm(Term,Term) 5 6 8
code.Extracter.getSimplified(Term,Term) 4 8 10
code.Extracter.getTheDeltaFactors(Factor,Factor) 6 6 6
code.Factor.Factor(BigInteger) 1 1 1
code.Factor.getDegree() 1 1 1
code.Factor.upToTerm() 1 1 1
code.Parser.Parser(String) 3 1 3
code.Parser.checkBigDegree(BigInteger) 1 1 1
code.Parser.clearBlank() 1 2 2
code.Parser.getChar(int) 2 2 3
code.Parser.getCosFactor() 3 1 3
code.Parser.getDegree() 2 2 2
code.Parser.getFactor() 6 6 6
code.Parser.getInnerFactor() 5 2 5
code.Parser.getPoly() 2 6 7
code.Parser.getPolyFactor() 1 1 1
code.Parser.getPowerFactor() 4 3 4
code.Parser.getSinFactor() 3 1 3
code.Parser.getTerm() 4 6 8
code.Parser.staticCheck(String) 3 3 5
code.Poly.Poly(ArrayList) 1 1 1
code.Poly.canDownToTerm() 1 1 1
code.Poly.cpTerms() 1 1 1
code.Poly.deleteUseless() 1 4 4
code.Poly.differential() 1 2 2
code.Poly.downToTerm() 3 2 3
code.Poly.equals(Object) 9 3 10
code.Poly.getTerms() 1 1 1
code.Poly.mergeSameTerm() 1 4 4
code.Poly.optimization() 1 2 2
code.Poly.toString() 3 3 4
code.PolyException.PolyException() 1 1 1
code.PolyFactor.PolyFactor(Poly) 1 1 1
code.PolyFactor.canExtract(Factor) 1 1 1
code.PolyFactor.canMerge(Factor) 1 1 1
code.PolyFactor.cpInnerPoly() 1 1 1
code.PolyFactor.differential() 1 1 1
code.PolyFactor.equals(Object) 4 4 6
code.PolyFactor.extract(Factor) 1 1 1
code.PolyFactor.getInnerPoly() 1 1 1
code.PolyFactor.merge(Factor) 1 1 1
code.PolyFactor.optimization() 3 3 3
code.PolyFactor.toString() 1 1 1
code.PowerFactor.PowerFactor(BigInteger) 1 1 1
code.PowerFactor.canExtract(Factor) 3 2 4
code.PowerFactor.canMerge(Factor) 2 1 2
code.PowerFactor.differential() 1 1 1
code.PowerFactor.equals(Object) 2 1 2
code.PowerFactor.extract(Factor) 2 2 2
code.PowerFactor.merge(Factor) 1 1 1
code.PowerFactor.optimization() 1 1 1
code.PowerFactor.toString() 2 2 2
code.SinFactor.SinFactor(BigInteger) 1 1 1
code.SinFactor.SinFactor(BigInteger,Factor) 1 1 1
code.SinFactor.canExtract(Factor) 5 4 7
code.SinFactor.canMerge(Factor) 4 4 6
code.SinFactor.differential() 3 2 3
code.SinFactor.equals(Object) 5 4 7
code.SinFactor.extract(Factor) 1 2 2
code.SinFactor.getInnerFactor() 1 1 1
code.SinFactor.merge(Factor) 1 1 1
code.SinFactor.optimization() 2 2 3
code.SinFactor.toString() 4 4 4
code.Term.Term(ArrayList) 1 1 1
code.Term.Term(BigInteger) 1 1 1
code.Term.Term(BigInteger,ArrayList) 1 1 1
code.Term.Term(Factor) 1 1 1
code.Term.canDownToFactor() 4 2 5
code.Term.canMerge(Term) 11 7 12
code.Term.compareTo(Object) 1 1 1
code.Term.containOnlyPolyFactor() 6 3 6
code.Term.cpFactors() 1 1 1
code.Term.cpSelf() 1 1 1
code.Term.deleteUseless() 1 3 3
code.Term.differential() 1 4 4
code.Term.downToFactor() 6 3 7
code.Term.getCoe() 3 1 3
code.Term.getFactors() 1 1 1
code.Term.getFactorsSize() 1 1 1
code.Term.merge(Term) 1 1 1
code.Term.mergeConstant() 1 4 5
code.Term.mergeSameFactor() 1 4 4
code.Term.mult(BigInteger) 1 1 1
code.Term.mult(Factor) 1 1 1
code.Term.mult(Term) 1 1 1
code.Term.optimization() 5 7 7
code.Term.toString() 9 9 11
code.Term.upToPoly() 1 1 1
code.Test.main(String[]) 1 3 3
code.Test.outputLogic(Poly) 1 2 2
Class OCavg WMC
code.BigDegreeException n/a 0
code.BracketException n/a 0
code.Calculator 2.11 19
code.ConstantFactor 1.73 19
code.CosFactor 2.73 30
code.Extracter 7 28
code.Factor 1 3
code.FactorException n/a 0
code.FormatException n/a 0
code.Parser 3.5 49
code.Poly 3 33
code.PolyException 1 1
code.PolyFactor 1.45 16
code.PowerFactor 1.67 15
code.SinFactor 2.73 30
code.Term 3.12 78
code.Test 2 4

这次的复杂度主要在于Parser,Extracter,Term这三个类。

Parser中,我用了递归下降的操作,第一次使用这个方法,写得比较面向过程,也比较丑。

Extracter这个类是我周二晚上得知延时的消息后,连夜赶工的一个提公因式的类,时间紧促,就没有做太多的设计,导致比较丑。

Term类的设计中,我欠考虑了。本次我的主要优化操作是通过把Term转换成Poly或者转换成Factor来完成的,这需要很多的判定条件,我虽然把计算等方法提取出来了,但是这些判定方法我仍留在Term中,导致了较大的复杂度,这是设计上的败笔。

难点分析

输入处理

当加入了嵌套这一输入格式之后,单用正则表达式已经难以实现输入处理了。受同学启发,我这次采用了递归下降的方法并结合正则表达式进行输入处理,利用正则表达式的group()来辅助递归下降的偏移量计算。效果相对较好。

由于第一次使用递归下降,在实现阶段,bug不断,经常出现漏加偏移量等BUG,并导致我在中测中浪费了大量提交机会。

优化输出

这一次优化的重点在于去掉那些层次太深,且毫无意义的括号,本人这次的优化还算可以,能够去除掉多数情况的无意义括号。

BUG分析

本人的BUG

1615461-20190326182102221-1477712687.png

这次本人测试自己代码的时候没有像以前那样先把数据生成器写好再进行测试,而是上来就手动测试并提交,导致BUG不断。

不过这些BUG都在于输入处理的递归下降之中,其他部分倒还没有找到bug。

不过,得益于那几天的血泪,公测、互测还是满血过关。

此外,这次我的自动化测试还出了些小插曲,留到后头再说。

总得看来,这次课下测试真的把我打怕了,直到强测结果出来的时候才松了口气。

本人以后争取保证每次都做到“测试先行

同屋BUG

1615461-20190326182112342-1284794236.png

这次遇到的BUG又回到了输入处理上,这个也就没啥好说的了,递归下降处理这种多项式要注意的点还是挺多的,没有自动化测试的情况下出现输入处理上的bug也很常见,虽然本不应该(

别人提及的BUG

在本次作业的深夜爆肝中,我也时不时地和一些同学交流问题。

其中一位同学就提及了我之前说的共享对象的可变性导致的BUG。现在我们面对共享对象出现的BUG的根源主要在于“对象”,“引用”没有分清,“深拷贝”,“浅拷贝”没分清,我个人的建议是,用不可变对象,每次计算或求导之后返回一个新的对象,而不在原有对象上修改,并利用JAVA的垃圾回收处理不被引用的对象。(想想BIgInteger的计算,类似)

目前,我们对于共享对象的问题主要在于是否可变,下一单元的多线程中共享对象怕是更加麻烦。(哭辽

狼灭

狼屋友

今年和往年不一样,不再是一对一的solo,而是八人圣杯战争。

如果还像往年那样,逐字逐句地挑,显然有点太耗时间,且不太值得。

测试方法很简单,评测姬先每人扫上50份随机数据,如果有比较naive的bug的话,此时基本上就已经倒了。

之后对那些能顶50次的代码再进行千份级别的扫射,同时,自己打开代码看看有没有一些常见的BUG,并把自己曾出过问题的数据放进去测试。

如果这样子还没找到BUG,那大概率是没啥BUG了,不过总有例外,比如,我在自我测试的时候就扫出来过自己的一个BUG,是一个括号匹配问题的BUG,BUG很naive,但是我没注意到,一般情况下也不会生成那样的数据(比如") 1 ("识别不出来错误),扫射了几万份数据后终于查出。

所以对于那些足够“硬”的代码,我一般是睡觉的时候扫上个几万份,如果再没有BUG,就根据时间安排,决定是否要逐字逐句地找BUG。

搭个评测姬

结构

  1. 数据生成器
  2. 待测程序
  3. 标准程序
  4. Judge
  5. 结果存储

这其中,每次作业都可以复用的是待测程序,标准程序的调用方法,结果存储的文件结构。

那么,为了代码复用,简化开发流程(留下了每次用C++从头rush评测姬的血泪)我利用java的继承和多态机制

实现了快速改装的评测姬。

下面简要介绍一下一些可能不那么丑的设计思路

1.文件结构

1615461-20190326182124323-2134381571.png

评测姬具有初始化文件夹与评测日志的方法。

每个从者对应一个文件夹,Ruler作为标程。

每个文件夹下存放对应从者的代码文件夹,以及各种测试数据的文件夹

1615461-20190326182132432-1430995553.png

各种测试数据的文件夹内存放着对应数据的测试结果以及统计信息。

2.测试数据生成器

测试数据生成器实现一个统一的“供弹”接口,使得我们的评测姬可以选择不同类型的测试数据。

这么做至少有一个好处,比如说,一个人的程序会在幂的整数带有正号的情况下崩溃,但是你又想找到他运算的BUG。那么,就可以为他设计一个幂不带正号的测试数据,从而便于区分非同质BUG。

强调一点

利用评测姬进行扫射的基础是你的生成器足够强大,能够生成出命中对方思维漏洞的数据(为什么不说实现时的手残呢? 因为,这年头谁还没个评测姬了?)这也就要求,对方的思维并没有达到基本正确性的要求,并且你达到了。那么,你的数据生成器才是具有效力的。

此外,评测姬绝不仅仅是用来狼人的,很多时候是用来狼自己的,可以检查出自己思维或者实现的错误。

面向对象的一些学习经验

这部分我将以我回答一些我思考过的问题的方式来呈现。

为什么要面向对象

把问题用一个个点(对象)之间的联系方式来体现,从模型的构建上来看,比较方便且直观。

对此,我就想到了寒假摸得一个叫ROS的东西,那个机器人操作系统就是基于各个点之间的通信来实现一个完整的功能,比方说,我现在想写一个自动驾驶(差速驱动转向)的程序,用java来写,我得有个轮子类,一个引擎类,一个地图类,一个雷达类,一个调度类,这些类组合成了一个新的类——小车,叫他小车有点low,那就叫他Pusher叭~,这个Pusher内部包含着上述几个类。在Ros中,体现为,引擎节点,雷达节点,调度节点等等。

在JAVA中,对象之间的联系由传参,返回值,共享对象之类的方式,ROS中也有请求服务方式(传参,返回值),话题方式(共享对象)。

说了这么多看似没啥关系的东西,我主要想说明一件事,我认为面向对象的作用主要体现在便于我们把现实中的问题抽象出一个更贴近现实的模型,从而在思维上解决起来更加容易。

关于创建模式

前三次作业中,对象的复杂度越来越高,如果仍然把对象的创建通过一个简简单单的 new() 来实现,会导致,功能与创建过度耦合,代码复杂度骤然提高。于是我在第三次作业中把整个多项式最复杂的创建方式——字符串解析方式交由Parser实现。从而降低了复杂度。

此外,对于那些具有众多同类型属性、相似的不同类我们可以采用工厂的方式来创建新的对象,从而,进一步将对象的构造与功能分离,降低耦合度。

转载于:https://www.cnblogs.com/BerserkerRuaaaaaaa/p/10602080.html

你可能感兴趣的文章
主成分分析(PCA)原理详解
查看>>
短信验证接口网址
查看>>
Geohash距离估算
查看>>
Demon_背包系统(实现装备栏,背包栏,可以切换装备)
查看>>
记录:一次数据库被恶意修改配置文件的问题
查看>>
redis 持久化
查看>>
解决Jupyter notebook[import tensorflow as tf]报错
查看>>
Windows平台下使用ffmpeg和segmenter实现m3u8直播点播
查看>>
python网络画图——networkX
查看>>
ubuntu16.04文件形式安装mongodb
查看>>
SpringBoot------ActiveMQ安装
查看>>
详细了解 int? 类型
查看>>
字符串匹配 ?kmp : hash
查看>>
mongod.service: control process exited, code=exited status=1
查看>>
c# 发送邮件、附件 分类: C# 2014-12-...
查看>>
对360来说,江湖上再无“搜狗”这个传说
查看>>
composer
查看>>
OpenCV特征点检测——ORB特征
查看>>
mysql的csv数据导入与导出
查看>>
leetcode笔记:Pascal's Triangle
查看>>