C# 如何提高圈复杂度?

C# 如何提高圈复杂度?,c#,design-patterns,complexity-theory,cyclomatic-complexity,C#,Design Patterns,Complexity Theory,Cyclomatic Complexity,对于包含大量决策语句(包括if/while/for语句)的方法,圈复杂度将很高。那么我们如何改进它呢 我正在处理一个大项目,我应该减少CC>10的方法的CC。解决这个问题有很多方法。下面我将列出一些代码模式(不是实际的代码)以及我遇到的问题。它们是否可能被简化 导致许多判决陈述的案例示例: 案例1) 案例2) 您应该使用重构来减少CC 条件多态代码与多态代码的区别在于,在多态代码中,决策是在运行时做出的。这使您可以更灵活地添加\更改\删除条件,而无需修改代码。您可以使用单元测试单独测试这些行为,

对于包含大量决策语句(包括if/while/for语句)的方法,圈复杂度将很高。那么我们如何改进它呢

我正在处理一个大项目,我应该减少CC>10的方法的CC。解决这个问题有很多方法。下面我将列出一些代码模式(不是实际的代码)以及我遇到的问题。它们是否可能被简化

导致许多判决陈述的案例示例:

案例1)

案例2)


您应该使用重构来减少CC

条件多态代码与多态代码的区别在于,在多态代码中,决策是在运行时做出的。这使您可以更灵活地添加\更改\删除条件,而无需修改代码。您可以使用单元测试单独测试这些行为,从而提高可测试性。此外,由于将有较少的条件代码,这意味着代码易于阅读,CC较少

有关更多信息,请查看esp

我会这样做的第一个案例,以消除条件,从而CC。此外,代码更面向对象、可读性和可测试性

void Main() {
    var objectA = GetObjectA();
    objectA.DoMyTask();
}

GetObjectA(){
    return If_All_Is_Well ? new ObjectA() : new EmptyObjectA();
}

class ObjectA() {
    DoMyTask() {
        var objectB = GetObjectB();
        var objectC = GetObjectC();
        objectC.DoAnotherTask();     // I am assuming that you would call the doXXX or doYYY methods on objectB or C because otherwise there is no need to create them
    }

    void GetObjectC() {
        return If_All_Is_Well_Again ? new ObjectC() : new EmptyObjectC();
    }
}

class EmptyObjectA() { // http://en.wikipedia.org/wiki/Null_Object_pattern
    DoMyTask() {
        doZZZZ();
    }
}

class ObjectC() {
    DoAnotherTask() {
        doXXX();
    }
}

class EmptyObjectB() { 
    DoAnotherTask() {
        doYYY();
    }
}
在第二种情况下,执行与第一种情况相同的操作

在第三种情况下—

var myCriteria = GetCriteria();
if(myCriteria.Contains(curretnCase))
    doStuff();

IEnumerable<Names> GetCriteria() {
   // return new list of criteria.
}
var myCriteria=GetCriteria();
if(myCriteria.Contains(currentcase))
doStuff();
IEnumerable GetCriteria(){
//返回新的条件列表。
}

案例1只需将其重构为更小的函数即可解决此问题。例如,以下代码段可能是一个函数:

objectC = doThatMethod();
if(objectC != null)
{
 doXXX();
}
else{
 doYYY();
}
案例2-完全相同的方法。将else子句的内容取出到一个较小的helper函数中

案例3-列出要检查的字符串,并创建一个小的辅助函数,将字符串与许多选项进行比较(可以使用linq进一步简化)


情况2中的最后一个if可以简化:

 if(isTrue)
 {
  if(e > 1)
  {
可以用

if(isTrue && (e>1))
案例3可以改写为:

new string[]{StringConstants.AAA,...}
    .Contains(e.PropertyName)
您甚至可以将字符串数组制作成一个
哈希集
,以获得O(1)性能。

我不是C#程序员,但我会尝试一下

在第一种情况下,我要说的是,对象首先不应该为null。如果这是不可避免的(通常是可以避免的),那么我会使用早期回报模式:

if ( objectA == NULL ) {
    return;
}
// rest of code here
第二种情况显然是不现实的代码,但我至少更愿意说:

if ( isTrue && e > 1 ) {
    DoStuff();
}
而不是使用两个单独的ifs

在最后一个例子中,我将把要测试的字符串存储在数组/向量/映射中,并使用容器方法进行搜索


最后,虽然使用圈复杂度是“一件好事”(tm),我自己也使用它,但有些函数自然会有点复杂-验证用户输入就是一个例子。我经常希望我使用的CC工具(Source Monitor at-free,非常好)支持一个白名单,其中的函数我知道一定很复杂,我不希望它标记。

对于案例1和案例2。。。有时,国际单项体育联合会有许多层面,而且都以某种方式相互关联,因此很难分割。对于案例3。。。这是一个好主意,但我可能在相同的方法中有许多这样的检查,如果我使用这种方法,意味着我需要许多这样的数组,并且ifs的数量也不会减少,例如if(…){}else if(…){}else if(…){else if(…){else if(…){yeen:减少圈复杂度通常会导致更多的代码。然而,所有的新代码都在做一项非常具体的工作,并且可以很容易地进行单元测试。不要在方法中创建对象,而是查看依赖项注入/构造函数注入,看看是否可以传递它们。如果需要,查看工厂或构建器模式以创建复杂对象等。。这一切都取决于你的具体情况,但如果你发现层是紧密耦合和相互关联的,那么你的架构是不好的,因为不应该是这样的。不太理解你对案例1的建议。。。那么什么是“引用[Null对象模式][2]”呢。。对格式错误表示歉意。链接在这里-。空对象模式是关于封装处理类中对象的可空性的行为。因此,您的代码中不会出现空检查。通过这种方式,您可以避免空检查的if条件,这是使用多态性替换条件逻辑的一种情况。请检查,“引用[Null对象模式][4]中的数字4”指的是wiki中的某个地方?对不起,我没听清楚。为什么有两个EmptyObjectA类,应该有EmptyObjectB和EmptyObjectC吗?因为当objectA为null时,它什么也不做。GetObjectB()在哪里?我仍然感到困惑……尽管简化的if语句(对于案例2)不会降低方法的圈复杂度。
if(isTrue && (e>1))
new string[]{StringConstants.AAA,...}
    .Contains(e.PropertyName)
if ( objectA == NULL ) {
    return;
}
// rest of code here
if ( isTrue && e > 1 ) {
    DoStuff();
}