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