OO第一单元总结
OO第一单元总结
程序结构分析
-
第一次作业
Method ev(G) iv(G) v(G) MainClass.input_manage(String) 1 1 1 MainClass.main(String[]) 4 9 10 Poly.Poly(BigInteger,BigInteger) 1 1 1 Poly.add(BigInteger) 1 1 1 Poly.diff() 1 1 1 Poly.getCoeff() 1 1 1 Poly.out() 4 10 13 Class OCavg WMC MainClass 5 10 Poly 2.8 14 可以看出,第一次作业本身并不复杂,最复杂的部分主要是MainClass类的main函数内处理读取幂函数因子的过程和Poly类的out函数处理优化输出的过程。整体来说程序的扩展性和维护性都不好,也没有处理输入格式错误的情况,鲁棒性不高。
-
第二次作业
Method ev(G) iv(G) v(G) Cos.Cos(BigInteger) 1 1 1 Cos.diff() 1 1 1 Cos.getExp() 1 1 1 Cos.out() 3 2 3 MainClass.extract(String[]) 4 4 5 MainClass.input_manager(String) 2 6 6 MainClass.main(String[]) 1 9 9 MainClass.scanFactors(String[],Pattern,Pattern,Pattern,Pattern) 7 9 11 Power.Power(BigInteger) 1 1 1 Power.diff() 1 1 1 Power.getExp() 1 1 1 Power.out() 3 2 3 Sin.Sin(BigInteger) 1 1 1 Sin.diff() 1 1 1 Sin.getExp() 1 1 1 Sin.out() 3 2 3 Term.Term(BigInteger,Power,Sin,Cos) 1 1 1 Term.diff() 1 4 4 Term.out() 6 11 17 Term.plus(Term) 1 1 1 Class OCavg WMC Cos 1.5 6 MainClass 7 28 Power 1.5 6 Sin 1.5 6 Term 4 16 显然第二次作业提高了要求,加入了错误格式和三角函数。可以看出,第二次作业的架构并不是很好,首先是Power,Sin,Cos这三个类性质很相似,出现了大量的重复代码。这三类也没有和Term类形成继承或实现关系,层次结构不佳。
而Term类的定位并不是很清晰,此处的Term类似于一个四元组,由系数、幂函数、Sin、Cos构成,而仔细分析这四个类会发现如果顺着四元组的思路做这次作业,实际上只需要Term一个类即可,由四个BigInteger组成系数和三个函数的指数,不必大费周章再建三个类。这是第二次作业最简单的思路,但扩展性很差,面对增加嵌套和括号的表达式无能为力,整个优化也要全部重写,因此并不是一个好设计。而如果舍弃Term类,思路就是建立表达式树,递归完成求导。很明显,这一方案能够应对未来增添更多类函数更多种组合,是扩展性好的设计,这在第三次作业中得到了体现。
由于加入了三角函数和错误格式,第二次的MainClass类明显比第一次复杂许多,第一次尚且能够一main到底,第二次显然复杂许多。这一次的设计依然有浓重的面向过程的味道,main函数依然承担了大量的表达式解析判断和输出的工作,整个过程逻辑不是很清晰,分工不够明确。
虽然第二次作业互测没被测出bug,但强测过程中还是因为WF判断的一个疏忽导致WA了一个点。其实这些都是细节问题,更大的问题还是设计架构不佳,导致第三次作业整体重构。
-
第三次作业
Method ev(G) iv(G) v(G) CosNode.CosNode(BigInteger,ExpNode,String) 1 1 1 CosNode.diff() 1 1 1 CosNode.getContent() 1 1 1 Input.build(String) 3 4 5 Input.check(String,String) 5 9 10 Input.createFac(String,Matcher,Matcher,Matcher,Matcher,int,ArrayList ) 7 10 12 Input.createSign(String) 4 2 4 Input.getOrder(String) 5 2 5 Input.handleSign(int,int,String,ArrayList ,ArrayList ) 5 5 5 Input.rpfactor(String) 4 7 9 Input.simplify(String) 1 6 7 MainClass.main(String[]) 1 3 3 MinusNode.MinusNode(ExpNode,ExpNode,String) 1 1 1 MinusNode.diff() 1 1 1 MultNode.MultNode(ExpNode,ExpNode,String) 1 1 1 MultNode.diff() 1 1 1 NumNode.NumNode(BigInteger,SignNode,String) 1 1 1 NumNode.diff() 1 1 1 PlusNode.PlusNode(ExpNode,ExpNode,String) 1 1 1 PlusNode.diff() 1 1 1 PowNode.PowNode(BigInteger,String) 1 1 1 PowNode.diff() 1 1 1 SignNode.SignNode(String,ExpNode,ExpNode,String) 1 1 1 SignNode.getContent() 1 1 1 SignNode.getLexp() 1 1 1 SignNode.getRexp() 1 1 1 SignNode.getSign() 1 1 1 SignNode.setLexp(ExpNode) 1 1 1 SignNode.setRexp(ExpNode) 1 1 1 SinNode.SinNode(BigInteger,ExpNode,String) 1 1 1 SinNode.diff() 1 1 1 SinNode.getContent() 1 1 1 TermNode.TermNode(BigInteger,ExpNode,String) 1 1 1 TermNode.getContent() 1 1 1 TermNode.getExpo() 1 1 1 TermNode.getNest() 1 1 1 Class OCavg WMC CosNode 1 3 Input 6 48 MainClass 3 3 MinusNode 1 2 MultNode 1 2 NumNode 1 2 PlusNode 1 2 PowNode 1 2 SignNode 1 7 SinNode 1 3 TermNode 1 4 第三次作业加入了嵌套结构和括号,难度明显上升。这一次的设计相比前两次,结构层次就要好很多,通过定义表达式接口,因子类和符号类两个抽象类,体现出构建表达式树的层次来。通过表达式树这一个数据结构,很容易地完成了带有嵌套和括号的复杂表达式格式判断和求导运算。
因为没有优化所以复杂度低。同时,这次作业相比前两次可以发现,整个输入过程需要进行的化简判断构建表达式树的操作全部被封装在了Input类中,这使得main函数逻辑行为清晰明了,便于维护。而Input类里面也把输入的过程拆分为判断WF,替换因子,化简表达式,构建表达式树这几个部分,分工明确。
当然,对于具体的处理输入函数,构建表达式树的过程本身是确实是复杂的。
-
总结
从这三次作业来看,整个程序的结构在不断的优化,层次感越来越明显,逻辑不断清晰。扩展性和鲁棒性也越来越好。质量还是在不断提升的。
bug分析
-
第一次作业
强测全部通过。互测被找出一个优化过程的bug,出现在系数为1和-1的情况下,表达式前只省去了1而漏掉了*号,导致输出格式错误。
互测过程采用自动评测机随机生成数据测试其他人代码,并未测出bug,但仔细分析后发现了和我一样的bug,这说明第一次搭建的评测机还有待改进,需要增加一些侧重偏向,不能完全随机;也可以看出有些同学和我一样容易在优化细节上出现错误。
-
第二次作业
检测WF的程序出现了问题,来源于正则表达式的书写错误地使用了''|'',导致WF判断出错。
而在互测过程中,评测机经过改进,从小量级特殊数据到大量级普通数据均可以生成,覆盖性更好,也测出了其他人的bug,主要出现在输出符号的问题,正负号出现了错误。
-
第三次作业
第三次作业自己没有做足测试,导致强测错了很多点,实属不应该。一个问题是替换常数的符号后没有替换回来,导致BigInteger方法异常;其次是构建表达式树的过程中没有及时更新被替换的表达式因子,导致构建的表达式树出错;而求导方法本身也存在问题,一个是减号节点的求导后一表达式没有加括号,导致符号不对,另一个是嵌套表达式在提取内容的过程中缺少括号导致提出内容出错,求导错误。事实证明,还是需要给自己做足测试才能确保强测不会太差。
心得体会
三次作业,写了三份架构完全不同的代码,确实体会到了架构的重要性。在构思程序解决当前问题的同时,还要考虑未来应对更多更复杂的需求,如何构建才能便于未来增添新的需求和模式。
在这几周的学习实践中,我明白了通过接口和继承关系,使得程序设计具有层次,能够将不同但相似的类统一起来,使得主程序能够对一个统一的接口进行操作,而不用去对每一个具体的类进行操作,使主程序逻辑简洁,便于维护。其次是通过工厂模式,为创建类提供统一的方法,面对未来增加更多的函数种类,可以很方便的进行扩展。
通过三次作业的迭代开发,我充分认识到了应该如何设计,才能够使得代码具有良好的扩展性维护性,希望接下来的单元学习,能够不断努力,构建便于迭代开发的程序架构。