oo第一单元总结报告

OO第一单元总结报告

第一单元主要是完成了读入表达式并进行求导输出的任务,在作业中将其分为读入和处理两个部分,分别建立相应的类。

读入部分建立了FuncReader类(相当于工厂),其中内置了一些read方法,用来解析表达式的不同部分,各read方法之间递归调用,违法就返回null;这部分主要采用面向过程的思想,直接按步骤走就可以。

处理部分首先建立了Func接口,以实现求导和化简功能。然后针对x、sin(x)、cos(x)、c分别设计了基本函数类:PowerFunc,SinFunc,CosFunc,ConstFunc。接着针对组合情况:多个函数相加,多个函数相乘,两个函数复合;分别设计了AddFunc,MultFunc,CompoundFunc。其中AddFunc和MultFunc为了便于化简,采用了Map<Func, BigInteger>的形式,保证了同类项的系数和并和同类项的指数合并。各函数求导和化简方法都是返回一个新的函数,即把这些函数类看成不变类来使用。

输出部分直接在各个函数类中覆写toString方法即可。

基于度量的程序结构分析

类的属性表格

各缩写含义如下:

度量 意义 英文名称
Dcy 直接依赖的数量 Number of dependencies
B 可能的bug数 Halstead Bugs
v(G) 本质循环复杂度 Essential Cyclomatic Complexity
Cons 构造器数量 Number of constructors
LOC 代码行数 Lines of Code
IF_NEST if语句的深度 Conditional nesting depth
LOOP_NEST 循环语句的深度 Loop nesting depth
CONTROL 方法控制语句数目 Number of Control Statements
NAAC 新增的字段数 Number of attributes added
NOAC 新增的方法数 Number of operations added
NOOC 覆写的方法数(除接口) Number of operations overridden
CSA 类属性数目 Class size (attributes)
CSO 类方法数目 Class size (operations)
ISO 接口方法数 Interface size(operations)

相应的类属性如下:

Class B Cons Dcy LOC NAAC NOAC NOOC CSO
AddFunc 1 1 3 145 1 6 3 10
CompoundFunc 0 1 6 62 2 2 3 6
ConstFunc 0 1 1 40 7 3 3 7
CosFunc 0 0 4 26 1 2 3 5
FuncReader 1 1 8 219 3 9 0 10
Main 0 0 2 16 0 1 0 1
MultFunc 2 1 3 203 2 7 3 11
PowerFunc 0 0 2 23 1 2 3 5
SinFunc 0 0 2 23 1 2 3 5
Interface ISO
Func 2

可以看到读入部分的FuncReader类有较多的依赖,因为要分情况调用各个函数的构造方法;而其他函数类的依赖大多是在求导和化简方法中,因为求导化简之后会出现新的函数类,需要调用其他类的构造方法。

相应的方法属性如下:

ooMetrics 周一 29 3月 2021 10:15:01 CST
Method CONTROL IF_NEST LOC LOOP_NEST v(G)
AddFunc.AddFunc() 0 0 3 0 1
AddFunc.add(Func,BigInteger) 1 1 10 0 3
AddFunc.derivative() 4 1 16 1 5
AddFunc.equals(Object) 2 2 11 0 3
AddFunc.getMap() 0 0 3 0 1
AddFunc.hashCode() 0 0 4 0 1
AddFunc.isEmpty() 0 0 3 0 1
AddFunc.simplify() 15 2 62 2 15
AddFunc.size() 0 0 3 0 1
AddFunc.toString() 6 2 27 1 7
CompoundFunc.CompoundFunc(Func,Func) 0 0 4 0 1
CompoundFunc.derivative() 1 1 12 0 3
CompoundFunc.equals(Object) 2 2 12 0 4
CompoundFunc.hashCode() 0 0 4 0 1
CompoundFunc.simplify() 5 1 21 0 10
CompoundFunc.toString() 0 0 5 0 1
ConstFunc.ConstFunc(BigInteger) 0 0 3 0 1
ConstFunc.derivative() 0 0 4 0 1
ConstFunc.equals(Object) 2 2 11 0 3
ConstFunc.getVal() 0 0 3 0 1
ConstFunc.hashCode() 0 0 4 0 1
ConstFunc.simplify() 0 0 2 0 1
ConstFunc.toString() 0 0 4 0 1
CosFunc.derivative() 0 0 7 0 1
CosFunc.equals(Object) 0 0 4 0 1
CosFunc.hashCode() 0 0 4 0 1
CosFunc.simplify() 0 0 4 0 1
CosFunc.toString() 0 0 4 0 1
FuncReader.FuncReader(String) 0 0 5 0 1
FuncReader.readAddSub() 2 1 11 0 5
FuncReader.readBlank() 1 0 5 1 4
FuncReader.readDeg() 3 2 16 0 5
FuncReader.readExp() 9 2 49 1 10
FuncReader.readFactor() 13 2 60 0 18
FuncReader.readG() 2 2 16 0 5
FuncReader.readNum() 3 1 17 1 6
FuncReader.readSign() 1 1 9 0 4
FuncReader.readTerm() 4 1 26 1 6
Main.main(String[]) 1 1 14 0 2
MultFunc.MultFunc() 0 0 4 0 1
MultFunc.derivative() 6 1 28 2 7
MultFunc.equals(Object) 2 2 11 0 4
MultFunc.getCof() 0 0 3 0 1
MultFunc.getMap() 0 0 3 0 1
MultFunc.hashCode() 0 0 4 0 1
MultFunc.mult(Func,BigInteger) 3 1 21 0 5
MultFunc.setCof(BigInteger) 0 0 3 0 1
MultFunc.simplify() 17 2 60 2 17
MultFunc.size() 0 0 3 0 1
MultFunc.toString() 14 2 59 2 16
PowerFunc.derivative() 0 0 4 0 1
PowerFunc.equals(Object) 0 0 4 0 1
PowerFunc.hashCode() 0 0 4 0 1
PowerFunc.simplify() 0 0 4 0 1
PowerFunc.toString() 0 0 4 0 1
SinFunc.derivative() 0 0 4 0 1
SinFunc.equals(Object) 0 0 4 0 1
SinFunc.hashCode() 0 0 4 0 1
SinFunc.simplify() 0 0 4 0 1
SinFunc.toString() 0 0 4 0 1

类图

图中列出了类的主要字段和方法,其中mult()和add()方法为可变方法,derivative()和simplify()为不变方法

image

  • 优点:统一了接口;使用map容器便于合并同类项
  • 缺点:同一表达式有多种表示方式,存在多义现象;化简时为完成拆括号的工作,仍要判断具体类型

Bug分析

Bug 所在类和方法 LOC v(G)
读入时遇到括号未判断格式 FuncReader类,readExp()方法 49 10
化简时改变了ConstFunc的值 AddFunc类,simplify()方法 62 15
转化为字符串时出现(x+1)**2的情况 MultFunc类,toString()方法 59 16

对比不出现bug的方法可以看出,出bug的方法圈复杂度都比较高,这也是因为这几个方法都需要对类进行特判,导致分支语句增多,进而加大了bug出现的概率。

bug检测策略

第一次作业中直接通过手写的测评机(随机生成样例)+特殊样例来找bug;

第二、三次作业中直接用特殊样例找bug,或用一个函数多次求导的结果来当做复杂测试样例;

第一次作业通过大批量的测试+0等不容易生成的边际样例来保证有效性;

第二、三次作业中手动遍历构成情况来保证完备性,并通过多次求导的样例来保证复杂性;

以上测试均为黑盒测试策略,并未结合被测程序代码结构。

重构经历总结

2、3次作业重构前后的圈复杂度分别为3.49和3.38;可以看出圈复杂度有一定的下降,但是由于第三次作业加入了合并同类项的优化部分,所以复杂度还是比较高。2、3次作业类图的结构基本一致,只是各个类的具体组织方式由List变为了Map。第一次作业由于可以直接选用基底,比较特殊,故不作比较。

心得体会

  • in.hasNext()方法会阻塞!!!in.hasNext()方法会阻塞!!!in.hasNext()方法会阻塞!!!
  • str.charAt()会越界!!!str.charAt()会越界!!!str.charAt()会越界!!!
  • List很难化简(指二重循环还要判0,循环时改变List不太方便),而Map好化简
  • python写生成器和文件操作测试最方便!!!还可以用sympy等高级库来检查
  • 接口还是没用出灵魂来,这种架构感觉就像是强行类型转换了一样,具体化简情况还是要特判(当然求导就不用)
  • 从一开始最好就明确哪些方法是不变的,哪些是可变的,在不变方法中最好不调用可变方法(要调用的话就要先克隆对象)
  • String.startWith()方法可以方便地检查前缀(不用charAt)
  • 多义性造成了化简时需要特判,如何设计一种无多义的架构?(这里已经尽量把PowerFunc和SinFunc的系数提到MultFunc这一层了)
posted @ 2021-03-29 12:34  Kyle-Kirsten  阅读(90)  评论(1编辑  收藏  举报