代码的环复杂度(Cyclomatic complexity,有时也翻译成圈复杂度)是一种代码复杂度的衡量标准,在 1976 年由 Thomas J. McCabe, Sr. 提出。
来看看计算公式。
代码环复杂度 = E − N + 2
E = 程序控制流图中边的个数
N = 程序控制流图中点的个数
很容易得出这样的结论:代码环复杂度越高,越容易出 bug。
可以想象如果需要开发人员自己去把一段代码的控制流图画出来,然后去数图中边和点的个数,这种做法效率太低了也容易出错。
好消息是,有一款名为 Source Monitor 的免费软件,能够帮我们来度量 Java 代码的环复杂度。当然这款软件也支持 C++和 C#。
为了说明如何使用这款软件,我写了一段简单的 Java 代码。
package test;
import java.util.ArrayList;
public class monthTool {
static ArrayList<String> monthCollection = new ArrayList<String>();
public static void main(String[] args) {
monthTool tool = new monthTool();
tool.printV1(1);
tool.printV2(2);
tool.printV1(0);
tool.printV2(-1);
tool.printV3(3);
tool.printV3(13);
}
public monthTool(){
monthCollection.add("Invalid");
monthCollection.add("January");
monthCollection.add("Febrary");
monthCollection.add("March");
monthCollection.add("April");
monthCollection.add("May");
monthCollection.add("June");
monthCollection.add("July");
monthCollection.add("August");
monthCollection.add("September");
monthCollection.add("October");
monthCollection.add("November");
monthCollection.add("December");
}
public void printV1(int month){
System.out.println("Month is: " + getMonthNameV1(month));
}
public void printV2(int month){
if( month >= 1 && month <= 12)
System.out.println("Month is: " + getMonthNameV2(month));
else
System.out.println("Please specify a valid month");
}
public void printV3(int month) {
System.out.println("Month is: " + getMonthNameV3(month));
}
public String getMonthNameV2(int month){
if( month == 1)
return "January";
else if( month == 2)
return "Febrary";
else if( month == 3)
return "March";
else if( month == 4)
return "April";
else if( month == 5)
return "May";
else if( month == 6)
return "June";
else if( month == 7)
return "July";
else if( month == 8)
return "August";
else if( month == 9)
return "September";
else if( month == 10)
return "October";
else if( month == 11)
return "November";
else if( month == 12)
return "December";
else
return "Invalid";
}
public String getMonthNameV1(int month){
switch (month){
case 1:
return "January";
case 2:
return "Febrary";
case 3:
return "March";
case 4:
return "April";
case 5:
return "May";
case 6:
return "June";
case 7:
return "July";
case 8:
return "August";
case 9:
return "September";
case 10:
return "October";
case 11:
return "November";
case 12:
return "December";
default:
return "Invalid";
}
}
public String getMonthNameV3(int month){
try {
return monthCollection.get(month);
}
catch (java.lang.IndexOutOfBoundsException e){
return "Invalid";
}
}
}
复制代码
其中我用了三种不同的方式实现了同一个逻辑,将一个代表月份的整数转成了月份名称。
下面是 Source Monitor 的具体用法。
1. 创建一个新的项目:
这里能看到所有 Source Monitor 支持的编程语言。
2. 指定您本地的 Java 项目文件地址:
3. 指定您的 Java 项目文件夹内,您希望 SourceMonitor 计算哪些 Java 文件的环复杂度。
4. 点 OK,就可以开始扫描啦。
很快 Source Monitor 就将我们指定的 Java 文件的环复杂度计算完毕。点击菜单“Display Method Metrics”来查看结果:
从环复杂度扫描结果能看出,明显第三种从月份名称集合里通过 ArrayList 自带的 get 方法取得月份名称是最优的解法——环复杂度仅为 2。
也可以通过图表的方式更直观得看到方法的环复杂度比较:
X 轴的值代表每个方法的环复杂度,Y 轴代表这些环复杂度的不同值出现的次数。
比如下图的意思是,环复杂度为 1 的方法(X 轴刻度为 1 的节点)共有 4 个(Y 轴刻度为 4),环复杂度为 2 的方法(X 轴刻度为 2 的节点)有 1 个(Y 轴刻度为 1)。以此类推。
要获取更多 Jerry 的原创技术文章,请关注公众号"汪子熙"。
评论