OO第一单元作业总结
1、第一次作业
(1)分析类方法的复杂度

一些概念:在分析结果中可以看到ev, iv, v这几栏,分别代指基本复杂度(Essential Complexity (ev(G))、模块设计复杂度(Module Design Complexity (iv(G)))、圈复杂度Cyclomatic Complexity (v(G))。
ev(G)基本复杂度是用来衡量程序非结构化程度的,非结构成分降低了程序的质量,增加了代码的维护难度,使程序难于理解。因此,基本复杂度高意味着非结构化程度高,难以模块化和维护。实际上,消除了一个错误有时会引起其他的错误。
Iv(G)模块设计复杂度是用来衡量模块判定结构,即模块和其他模块的调用关系。软件模块设计复杂度高意味模块耦合度高,这将导致模块难于隔离、维护和复用。模块设计复杂度是从模块流程图中移去那些不包含调用子模块的判定和循环结构后得出的圈复杂度,因此模块设计复杂度不能大于圈复杂度,通常是远小于圈复杂度。
v(G)是用来衡量一个模块判定结构的复杂程度,数量上表现为独立路径的条数,即合理的预防错误所需测试的最少路径条数,圈复杂度大说明程序代码可能质量低且难于测试和维护,经验表明,程序的可能错误和高的圈复杂度有着很大关系。
通过复杂度分析可以看出做第一次作业时的自己是十分青涩的,还没有从大一时学习C语言的思维模式中跳出来,若不是舍友及时相救甚至想要一main到底。即便如此还是只写出了两个类,而且基本所有的核心功能都放在poly类里实现了,这也导致了Poly类一些方法复杂度是很高的,其中以CheckandConstructPoly尤甚,整个方法基本在用if else判断正则表达式匹配情况,而且嵌套了很多层。需要判断的情况很多,读起来也觉得很麻烦,或许可以把不同的情况都拆开写到不同的方法里。
(2)分析类的规模

本次作业由于我根本就不咋会写代码,所以代码行数控制的不算特别好。因为只有两个类,就导致需要实现核心功能的Poly类写得很长。
(3)画出类图

一个概念:虚线普通箭头,指向被调用者,其中带有create的虚线是指创建(new)被调用者
这次作业有一个比较大的问题就是把读入部分放在了主类Main里写,这样容易导致代码维护性降低。
2、第二次作业
(1)分析类方法的复杂度
| Calculate.Deri(BigInteger,BigInteger,BigInteger,BigInteger) | 8.0 | 11.0 | 11.0 |
| Calculate.GetMonoList(ArrayList) | 4.0 | 4.0 | 4.0 |
| Calculate.prin(BigInteger,BigInteger,BigInteger,BigInteger) | 1.0 | 10.0 | 10.0 |
| CheckOp.ChangeOp(String) | 1.0 | 5.0 | 5.0 |
| CheckOp.CheckPS(String) | 1.0 | 2.0 | 2.0 |
| Checkspace.CheckEmpty(String) | 1.0 | 2.0 | 2.0 |
| Checkspace.CheckSpace(String) | 1.0 | 7.0 | 8.0 |
| Factor.CheckFact(String[]) | 1.0 | 3.0 | 3.0 |
| Factor.GetFact(String[]) | 1.0 | 2.0 | 2.0 |
| Factor.GetFactKey(String[]) | 6.0 | 18.0 | 19.0 |
| Input.inp() | 1.0 | 1.0 | 1.0 |
| Input.toString(String) | 1.0 | 1.0 | 1.0 |
| Key.GetCosKey(BigInteger,BigInteger) | 1.0 | 1.0 | 1.0 |
| Key.GetKKey(BigInteger) | 1.0 | 1.0 | 1.0 |
| Key.GetSinKey(BigInteger,BigInteger) | 1.0 | 1.0 | 1.0 |
| Key.GetXKey(BigInteger,BigInteger) | 1.0 | 1.0 | 1.0 |
| Key.SendCoeff() | 1.0 | 1.0 | 1.0 |
| Key.SendCos() | 1.0 | 1.0 | 1.0 |
| Key.SendSin() | 1.0 | 1.0 | 1.0 |
| Key.SendX() | 1.0 | 1.0 | 1.0 |
| Main.main(String[]) | 1.0 | 2.0 | 2.0 |
| Mono.GetCheckMono(String) | 1.0 | 3.0 | 3.0 |
| Total | 37.0 | 79.0 | 81.0 |
| Average | 1.6818181818181819 | 3.590909090909091 | 3.6818181818181817 |
这次作业可以看到Calculate类里的Deri(求导)方法复杂度是很高的,主要问题可能是出在if else用的太多?(整个方法全都是if else判断,我不确定这样写是否有太大问题)其实整体求导的思路是较为清晰的,但当时由于时间较为仓促,在处理求导时没有深思熟虑,不知道自己怎么就想出来这么一个奇怪的for循环求导结构,而且情况特别容易考虑不全面,像里面一些判断系数指数是否为0或为1 的情况就很容易被忽略,我仍然能清晰地记得被这里的bug支配的恐惧,后期debug的时候也是醉生梦死(突然发现可以用用switch额)然后就是我一直存在的一个大问题,每次在通过正则表达式讨论不同情况时,会把正则表达式以及通过它要进行判断的情况都写在同一个方法里(感觉涉及到group这些就不太好传递到其他方法里,从而导致不好把一个方法拆分成几个小方法)从Factor类里也可以看到GetFactKey这个方法复杂度是极高的,或许可以把正则单独放在一个方法里进行匹配判断?对此我还没有找到特别好的解决方法。
(2)分析类的规模

本次作业代码长度控制还算合理,主要是求导和存储系数指数的类占了较多的行数。
(3)画出类图

这次作业基本上是一个类实现一个方法,输入、检查格式、将系数指数存入数组、求导、输出这几个方法都写成了单独的类且全部依次在主类Main中调用。现在有一个问题是不确定这种结构是否合理。几个类的关系是依托求导这一运算的顺序建立的,在Main类中一个个new(建立新对象)一个个调用是否是符合规范的呢?
以及本次作业还有一个问题就是每个类实现的功能并不明晰,在前期构思的时候就没有仔细思考需要哪些类实现哪些功能,写代码的时候会出现突发奇想把一个类的某个方法拆出来单独设成一个类。这又涉及另一个问题了:作业前期构思应该构思到什么程度呢,只是大致明白需要什么类什么方法就去写的话就很容易遇到不知道一个功能该写成一个单独的类还是一个类里的方法这种问题。(我不确定这种问题是不是很无足轻重,毕竟代码总是能写出来的,就是后期在debug时容易比较混乱,以及整体的结构可能看起来不够清晰优美)
3、第三次作业
(1)分析类方法的复杂度
| CheckFormat.Checkformat(String) | 1.0 | 4.0 | 4.0 |
| CheckSpace.CheckEmpty(String) | 1.0 | 2.0 | 2.0 |
| CheckSpace.Checkspace(String) | 1.0 | 2.0 | 2.0 |
| EmptyCheck.Emptycheck(String[]) | 1.0 | 3.0 | 3.0 |
| FactorCheck.check1(int,int) | 1.0 | 5.0 | 6.0 |
| FactorCheck.GetIndex() | 1.0 | 7.0 | 8.0 |
| FactorCheck.SendStr(String) | 1.0 | 1.0 | 1.0 |
| FactorHandler.Facthandle(String) | 1.0 | 14.0 | 16.0 |
| FactorHandler.FuckYouAsshole(int) | 1.0 | 7.0 | 7.0 |
| FactorHandler.Shit(String) | 3.0 | 7.0 | 8.0 |
| Handle.CheckFact() | 1.0 | 2.0 | 2.0 |
| Handle.Item(String) | 1.0 | 2.0 | 2.0 |
| Handle.Poly(String) | 1.0 | 2.0 | 2.0 |
| Input.inp() | 1.0 | 1.0 | 1.0 |
| Input.toString(String) | 1.0 | 1.0 | 1.0 |
| ItemHandler.GetFactorList() | 1.0 | 1.0 | 1.0 |
| ItemHandler.Itemhandle(String) | 1.0 | 5.0 | 6.0 |
| Main.main(String[]) | 1.0 | 2.0 | 2.0 |
| PolyHandler.GetItemList() | 1.0 | 3.0 | 4.0 |
| PolyHandler.Polyhandle(String) | 1.0 | 6.0 | 10.0 |
| PrintShit.CheckFact() | 1.0 | 5.0 | 5.0 |
| PrintShit.Item(String) | 1.0 | 2.0 | 2.0 |
| PrintShit.Poly(String) | 1.0 | 3.0 | 3.0 |
| Total | 25.0 | 87.0 | 98.0 |
| Average | 1.0869565217391304 | 3.782608695652174 | 4.260869565217392 |
本次作业复杂度较高的FactHandle方法全部是用if else进行正则表达式判断三角函数是否符合格式规范(还是列出我的代码吧,确实不知道这样写有没有什么大问题)
1 public int Facthandle(String fac) { 2 String s = fac; 3 str = fac; 4 5 m1 = p1.matcher(fac); 6 m2 = p2.matcher(fac); 7 m3 = p3.matcher(fac); 8 m4 = p4.matcher(fac); 9 10 if (fac.charAt(0) == '(') { //表达式因子 11 s = Shit(fac); 12 13 Handle handle = new Handle(); 14 handle.Poly(s); 15 16 count = -1; 17 } else if (m1.find()) { 18 if (m1.group(2) != null || m1.group(4) != null || 19 m1.group(8) != null || m1.group(11) != null || 20 m1.group(13) != null) { //2 4 8 11 13 21 coeff = -1; 22 } 23 count = 0; 24 flag = 0; 25 //factcheck.check1(flag); 26 } else if (m2.find()) { 27 if (m2.group(2) != null || m2.group(4) != null || 28 m2.group(8) != null || m2.group(11) != null || 29 m2.group(13) != null) { //2 4 8 11 13 30 coeff = -1; 31 } 32 count = 0; 33 flag = 1; 34 //factcheck.check1(flag); 35 } else if (m3.find()) { 36 count = 1; 37 str1 = m3.group(1); 38 str2 = m3.group(2); 39 str3 = m3.group(3); 40 } else if (m4.find()) { 41 count = 2; 42 } else { 43 44 System.out.println("WRONG FORMAT!"); //还有其他情况嘛 45 System.exit(0); 46 } 47 return count; 48 }
(2)分析类的规模

这次作业每个类长度控制的还行,由于前期没构思好就开始莽导致写到求导的时候出现了惊天大bug,FactorHandler写的乱七八糟狗屁不通的。
(3)画出类图

本次作业基本没怎么用到继承的思想,只在形式上继承了一下(虽然代码里出现了extends但根本没有用,由于时间有点紧没有仔细研究集成和接口)这次代码由于涉及到递归,本人属于递归必死类型,所以涉及递归的部分写的极为混乱,而且好几个wrong format的点都没有考虑到(比如sin(- 6)这种),涉及到括号嵌套的地方也写得不够完善,结果也是相当惨烈了。(只能说下次还是早点开始写8)
以及提一下互测阶段找bug,我一般在自己构思代码的时候就会列出一些自己觉得可能会容易被忽略的点(主要以格式判断为主),比如空格导致的wrong format问题,在互测阶段就会先测试这些点。在看对方代码的过程中如果发现代码中使用正则表达式枚举格式的情况较多,就会根据对方代码着重就各种错误格式的样例进行检查。

浙公网安备 33010602011771号