面向对象第一单元博客总结

前言:

第一单元可以说是我的噩梦开端了,我本人代码基础较差,加上假期没有好好预习,直到第一周的周五才开始着手构思本单元的任务,加之对于面向对象对不够充分的理解,导致了第一单元的前两次作业的溃败.为此我决定认真反思.并在逐步回顾过去三周代码的过程中总结自己的得与失。

  • Homework1分析

    总述:如下UML类图,第一次作业是基于第一次训练给出的代码完成的。本次实验简单变量只有x的幂次以及常数,较为简单。Dan为词法分析,Ju为语法分析,Expr对应表达式,Term对应项,Var对应简单变量。本次实验采取hashmap储存数据,原因在于最终结果要化简成(ax^b)的基本形式,而经过合并同类项后,b对应唯一的ax^b,使用hashmap便于检索,而在项阶段,由于并未得到唯一对应,暂时用hashset分析。而递归下降则是基于判别到'*'"+""-"等符号后转入下一级分析而实现。(例如项在添加因子时通过*号进行判别)。

    本次实验采用了Factor接口,定义了Expr与Var实现了Factor的setPower(基于表达式与简单变量因子共同的power属性),以及合算(calFactor)。本次实验总体思路并不难,对于我个人来说问题在于读取括号之后的词法分析与回退设计(我的方法要求回退),这带来了让我头疼的一系列bug。

hw1

 

Complexity metrics周二22 3月 2022 19:12:34 CST  
Method CogC ev(G) iv(G) v(G)
Dan.getDuan() 0 1 1 1
Dan.getNumber(int) 8 1 9 9
Dan.getPos() 0 1 1 1
Dan.getVar() 11 4 7 8
Dan.gonext() 4 2 5 5
Dan.numpos(int) 5 1 5 7
Dan.setDuan(String) 0 1 1 1
Dan.setInput(String) 0 1 1 1
Dan.setPos(int) 0 1 1 1
Expr.addTerm(Term) 0 1 1 1
Expr.calFactor() 41 2 13 14
Expr.iniExpr() 0 1 1 1
Expr.printExpr() 19 1 7 8
Expr.setPower(int) 0 1 1 1
Expr.setTerms(HashSet<Term>) 0 1 1 1
Ju.parseExpr() 3 1 4 4
Ju.parseFactor() 44 6 23 24
Ju.parseTerm(boolean) 1 1 2 2
Ju.setLittle(Dan) 0 1 1 1
MainClass.main(String[]) 0 1 1 1
Term.addFactor(Factor) 0 1 1 1
Term.cal() 56 3 14 14
Term.iniTerm() 0 1 1 1
Term.isNeg() 0 1 1 1
Term.setNeg(boolean) 0 1 1 1
Var.calFactor() 0 1 1 1
Var.getPower() 0 1 1 1
Var.getXishu() 0 1 1 1
Var.setPower(int) 0 1 1 1
Var.setXishu(BigInteger) 0 1 1 1
         
Class OCavg OCmax WMC  
Dan 2.33 5 21  
Expr 4.33 14 26  
Ju 5.75 17 23  
MainClass 1 1 1  
Term 3.6 14 18  
Var 1 1 5  

度量分析:观察以上数据可知,本实验中Ju(句法分析)与Expr(表达式)以及Term的类方法平均循环复杂度均较高,parseFactor 方法与getVar方法的非结构化程度均不理想,这也确实是我代码出错的原因,而这两段关键代码被函数反复调用,表示出某些地方耦 合度过高,这使我的程序受到了极大的影响。

bug分析:本次实验我最终并没有通过中测的进阶测试,在强测中更是出现了7个bug,经过我个人的查找,发现parseFactor方法 中,解析表达式因子完成后,我直接步进,而返回parseTerm时再次步进,导致“*”读取的失败,使得项中因子缺失。

优缺点点评:本次实验我的代码具有的优点可能就是方法极度白痴,简单分为若干类,对于类的属性以及功用分析简单明了。

缺点在于合并同类项等处存在大量代码复用问题,这个问题可以通过提炼为方法而解决,同时步进(gonext)方法过于单一,使用 时容易出现问题,程序鲁棒性差。

  • Homework 2 分析

    总述:如下UML类图,第二次作业是在第一次作业基础上迭代,表达式中支持自定义函数,求和函数,同时简单变量中引入了三角函数.变量最终化简形式为"a*x**b*sin()**c*cos()**d"其中sin与cos中内容为常数因子以及x的幂次。据此,我们仍然是使用hashmap来构造,但是此次b到最终式并非单一映射,故考虑将“b*sin()**c*cos()**d”作为索引的key,那么相应的,我们需要储存sin与cos的内部内容以及各自的指数,若使用两字符串两数字来表征处理较为困难,故考虑将“sin()**c*cos()**d”作为字符串信息处理,为Var(简单变量)新增tan(三角相关的字符串),建立索引Vardir。

    在总结上次失败的教训后,我拆分了计算时用到的加法与乘法方法(其实可以考虑对加法和乘法单独建类),即mult与add方法,而在解决简单变量相乘问题时,引入三角函数字符串处理方法,此处使用了正则表达式拆分并建立对应的sanjiaopipei的对象。简单但是为日后迭代带来不便。同时在句法分析(ju)中拆分了遇到表达式因子与简单因子时的不同方法,设立了使用正则分析的parseSincos方法。

    同时,由于自定义函数表达式也需要进行预处理,故将Mainclass中的预处理单独拆分为pre方法,为了解决“+--++-”等过度出现的问题,笔者使用正则表达式捕获组循环化简。而总结函数的若干属性,我们可以发现有函数名,形参列表与实参列表,函数表达式以及函数最终代换结果。在表达式中读取到函数时,我们先跳转到replacefun方法中,随后链接到相应函数(f,g,h或者sum)的替换方式,此处仍然使用正则表达式,因为本次函数的实参较为简单,故运行中没有出现错误。

     

     

     

    MethodCogCev(G)iv(G)v(G)
    Dan.getDuan() 0 1 1 1
    Dan.getNumber(int) 8 1 9 9
    Dan.getPos() 0 1 1 1
    Dan.getVar() 34 6 15 18
    Dan.gonext() 4 2 7 7
    Dan.numpos(int) 5 1 5 7
    Dan.setDuan(String) 0 1 1 1
    Dan.setInput(String) 0 1 1 1
    Dan.setPos(int) 0 1 1 1
    Expr.addTerm(Term) 0 1 1 1
    Expr.calFactor() 20 2 7 8
    Expr.easycal() 15 1 7 7
    Expr.iniExpr() 0 1 1 1
    Expr.printExpr() 22 1 8 9
    Expr.setPower(int) 0 1 1 1
    Expr.setTerms(HashSet<Term>) 0 1 1 1
    Function.funexp(HashMap<String, Function>) 0 1 1 1
    Function.getExpr() 0 1 1 1
    Function.getName() 0 1 1 1
    Function.parse(String) 4 1 4 4
    Function.replace(String) 3 1 4 4
    Function.sumreplace(String) 10 3 5 6
    Ju.classify() 16 9 13 14
    Ju.exprFactor() 5 1 4 4
    Ju.fuhao() 0 1 1 1
    Ju.parseExpr() 2 1 3 3
    Ju.parseFactor() 2 2 2 2
    Ju.parseSincos(String) 0 1 1 1
    Ju.parseTerm(boolean) 1 1 2 2
    Ju.setLittle(Dan) 0 1 1 1
    Ju.sincos(BigInteger) 5 1 4 4
    MainClass.main(String[]) 2 1 3 3
    MainClass.pre(String) 3 1 4 4
    MainClass.replacefun(String, HashMap<String, Function>) 40 6 17 18
    Term.addFactor(Factor) 0 1 1 1
    Term.cal() 11 4 4 5
    Term.iniTerm() 0 1 1 1
    Term.ml(HashSet<Var>, HashSet<Var>) 14 1 7 7
    Term.setNeg(boolean) 0 1 1 1
    Term.subcal() 3 1 3 3
    Var.calFactor() 0 1 1 1
    Var.getPower() 0 1 1 1
    Var.getTan() 0 1 1 1
    Var.getXishu() 0 1 1 1
    Var.mult(Var) 12 1 8 8
    Var.setPower(int) 0 1 1 1
    Var.setTan(String) 0 1 1 1
    Var.setVar(BigInteger, int, String) 0 1 1 1
    Var.setXishu(BigInteger) 0 1 1 1
    Vardir.add(Vardir) 12 1 8 8
    Vardir.equals(Object) 4 3 3 5
    Vardir.getPower() 0 1 1 1
    Vardir.getTan() 0 1 1 1
    Vardir.hashCode() 0 1 1 1
    Vardir.setPower(int) 0 1 1 1
    Vardir.setTan(String) 0 1 1 1
    sanjiaopipei.equals(Object) 4 3 3 5
    sanjiaopipei.getList() 0 1 1 1
    sanjiaopipei.getName() 0 1 1 1
    sanjiaopipei.hashCode() 0 1 1 1
    sanjiaopipei.set(String, String) 0 1 1 1
    sanjiaopipei.setList(String) 0 1 1 1
    sanjiaopipei.setName(String) 0 1 1 1
             
    Class OCavg OCmax WMC  
    Dan 3.22 13 29  
    Expr 4.62 9 37  
    Function 2.83 6 17  
    Ju 3.33 12 30  
    MainClass 6.33 12 19  
    Term 2.83 6 17  
    Var 2.3 8 23  
    Vardir 2.29 8 16  
    sanjiaopipei 1.29 3 9  

    度量分析:可以看到本次构造中MainClass的平均循环复杂度得到了很大的提升,这和pre方法的改变有较大的关联。由于我有意识的拆分,各类的类方法数量也有显著的提升。而方法中,词法分析中的getVar被大量关联,Expr中的calFactor以及printExpr以及MainClass中的函数代换,均存在非结构程度高,耦合复杂以及判断路径复杂的问题。这三个函数确实内部较为复杂,难以维护。

    bug分析:本次我仍然是很遗憾地没有通过中测,相比于上次错误两个点,这次仅有一处错误,课下发现原来是函数类在进行代换时,每次实参列表仅仅是增加新的实参进来,再从头开始计数匹配,如计算f(x)+f(x**2)时实际计算的是f(x)+f(x),没发现这个bug让我明白自己造数据能力较差,并且对指导书要求理解不深。

    优缺点点评:本次代码的优点在于相比于第一次的设计,方法拆分做的不错,也有了更加明确的完成目标,中间经历了三角函数相关内容的重构,也让我意识到对类属性认知的重要性。本次运用了正则表达式,便于分析,但是不支持嵌套,因而难以在此基础上实现迭代。

  • Homework 3 分析

    总述:如下UML类图,第三次作业相比于第二次作业增加的要求便是:支持多项式函数以及三角函数的嵌套,第二次使用正则表达式偷懒的我狠狠地吃了一瘪,不过这个问题还算好解决。将replacefu方法设置为递归调用,每一次递归都替换表达式中当前层次的函数因子。最后便可以展开为无函数表达式。而在sin,cos,sum等的匹配过程中,我将使用正则表达式的方法替换为了使用括号匹配判别的方法,保证了准确性。针对本次要求sin与cos中必须为因子,我又使用了halfprintf方法,完成了内部解析判别。就这样我顺利通过了第三次作业的中测,进入到了互测环节。

  

 

MethodCogCev(G)iv(G)v(G)
Dan.getDuan() 0 1 1 1
Dan.getNumber(int) 8 1 9 9
Dan.getPos() 0 1 1 1
Dan.getVar() 34 6 15 18
Dan.gonext() 4 2 7 7
Dan.numpos(int) 5 1 5 7
Dan.setDuan(String) 0 1 1 1
Dan.setInput(String) 0 1 1 1
Dan.setPos(int) 0 1 1 1
Expr.addTerm(Term) 0 1 1 1
Expr.calFactor() 20 2 7 8
Expr.easycal() 15 1 7 7
Expr.halfprintExpr() 35 1 13 14
Expr.iniExpr() 0 1 1 1
Expr.printExpr() 35 1 13 14
Expr.setPower(int) 0 1 1 1
Expr.setTerms(HashSet<Term>) 0 1 1 1
Function.funexp(HashMap<String, Function>) 0 1 1 1
Function.getExpr() 0 1 1 1
Function.getName() 0 1 1 1
Function.parse(String) 4 1 4 4
Function.replace(String) 24 1 8 10
Function.sumreplace(String) 26 5 9 12
Ju.classify() 16 9 13 14
Ju.exprFactor() 5 1 4 4
Ju.fuhao() 0 1 1 1
Ju.inout(String) 12 5 5 9
Ju.parseExpr() 2 1 3 3
Ju.parseFactor() 2 2 2 2
Ju.parseSincos(String) 0 1 1 1
Ju.parseTerm(boolean) 1 1 2 2
Ju.setLittle(Dan) 0 1 1 1
Ju.sincos(BigInteger) 8 1 6 6
MainClass.main(String[]) 1 1 2 2
MainClass.pre(String) 3 1 4 4
MainClass.replacefun(String, HashMap<String, Function>) 40 1 17 18
Term.addFactor(Factor) 0 1 1 1
Term.cal() 11 4 4 5
Term.iniTerm() 0 1 1 1
Term.ml(HashSet<Var>, HashSet<Var>) 14 1 7 7
Term.setNeg(boolean) 0 1 1 1
Term.subcal() 3 1 3 3
Var.calFactor() 0 1 1 1
Var.getPower() 0 1 1 1
Var.getTan() 0 1 1 1
Var.getXishu() 0 1 1 1
Var.halfprintExpr() 22 2 14 14
Var.mult(Var) 44 1 19 21
Var.setPower(int) 0 1 1 1
Var.setTan(String) 0 1 1 1
Var.setVar(BigInteger, int, String) 0 1 1 1
Var.setXishu(BigInteger) 0 1 1 1
Vardir.add(Vardir) 42 1 17 19
Vardir.equals(Object) 4 3 3 5
Vardir.getPower() 0 1 1 1
Vardir.getTan() 0 1 1 1
Vardir.hashCode() 0 1 1 1
Vardir.setPower(int) 0 1 1 1
Vardir.setTan(String) 0 1 1 1
sanjiaopipei.equals(Object) 4 3 3 5
sanjiaopipei.getList() 0 1 1 1
sanjiaopipei.getName() 0 1 1 1
sanjiaopipei.hashCode() 0 1 1 1
sanjiaopipei.set(String, String) 0 1 1 1
sanjiaopipei.setList(String) 0 1 1 1
sanjiaopipei.setName(String) 0 1 1 1
         
Class OCavg OCmax WMC  
Dan 3.22 13 29  
Expr 5.62 13 45  
Function 4.83 12 29  
Ju 3.9 12 39  
MainClass 6 12 18  
Term 2.83 6 17  
Var 3.5 17 35  
Vardir 3.57 17 25  
sanjiaopipei 1.29 3 9  

度量分析:可以看到本次构造中由于维护sin以及cos中内容为因子的halfprint方法的引入以及使用括号匹配带来的循环嵌套,Expr的类循环复杂度有所提升,递归也让Function类的循环复杂度上升。而本次由于halfprintf方法的引入,mult与add方法的耦合程度变得复杂,由于递归的存在,replace的耦合程度也大幅度增加。

bug分析与hack策略:本次程序的主要bug在于,输入的数字变量可能爆int,如sum中的上下限,对此,我使用long类型转化字符串即可。本次我hack时采用的策略是多层嵌套三角如sin(cos())+cos(sin(cos))同时内部设置过量+号,hack了两位同学。

优缺点点评:本次代码的优点在于相比于第二次的设计,使用了括号匹配代替正则,起到了很好的作用,缺点在于没有很好地提取括号匹配的公共方法,导致代码量较大。

第一单元总结语

第一单元教会了我什么呢?我最近一直在想这样的问题。答案是,经过两次惨痛的失败以及中间一次推倒重来,我终于学会了理智认真地去分析类与方法,我学会了把方法中的反复出现的代码提炼出来封装为新的方法的美观的设计思路,我明白了要先想好最终目标以及其形式再去采取行动的结果导向的思维。我希望通过对这门课的深入学习,我能获得更强的代码水平,去解决更加困难复杂的问题。

posted on 2022-03-26 10:06  酷clear  阅读(31)  评论(2编辑  收藏  举报