使用lambdas初始化列表会导致高IL圈复杂度:为什么以及如何消除这种复杂性?例如,下面的代码导致类的静态构造函数(实际上是编译器生成的)非常复杂:1 +列表计数.
static List<Predicate<string>> list = new List<Predicate<string>>()
{
s => s == null,
s=> s.StartsWith(“R”),
... With a lot of predicates like that ….
};
Run Code Online (Sandbox Code Playgroud)
注意:复杂度用计算 NDepend
只是想知道,我已经为Visual Studio安装了CodeMaid并在大型代码库上获得了代码质量统计数据.我看到许多方法的数字从1到300不等.多少是太多了?我们(或者我们应该)甚至可以为这样的事情设置门槛吗?
我使用下面的方法来检查null或空字段:
public boolean isVoNotNull(){
return null != this.cardNo && StringUtils.isNotBlank(this.cardNo)
&& null != this.otp && StringUtils.isNotBlank(this.otp)
&& null != this.password && StringUtils.isNotBlank(this.password)
&& null != this.userid && StringUtils.isNotBlank(this.userid)
&& null != this.type && StringUtils.isNotBlank(this.type)
&& null != this.walletMobileNo && StringUtils.isNotBlank(this.walletMobileNo);
}
Run Code Online (Sandbox Code Playgroud)
但是在使用SonarLint验证此代码时,请低于异常
EXCEPTION:此方法"isVoNotNull"的Cyclomatic Complexity为12,大于10授权.
如何解决此异常或如何从代码中删除复杂性?
我想减少开关情况的圈复杂度,我的代码是:
public String getCalenderName() {
switch (type) {
case COUNTRY:
return country == null ? name : country.getName() + HOLIDAY_CALENDAR;
case CCP:
return ccp == null ? name : ccp.getName() + " CCP" + HOLIDAY_CALENDAR;
case EXCHANGE:
return exchange == null ? name : exchange.getName() + HOLIDAY_CALENDAR;
case TENANT:
return tenant == null ? name : tenant.getName() + HOLIDAY_CALENDAR;
default:
return name;
}
}
Run Code Online (Sandbox Code Playgroud)
此代码块的复杂度为16,并希望将其降低到10。country,ccp,exchange和tenant是我的不同对象。基于类型I将调用它们各自的方法。
声纳给我以下圈复杂度数:22。
对于以下程序:
private static SomeDto checkSomething(AnotherDto anotherDto, String reference)
{
SomeDto someDto = new SomeDto();
// condition 1
if (!someDto.getA())
return new SomeDto("bla1", "blabla");
// condition 2
if (someDto.getName2() == null || checkSurName(anotherDto.getName()))
return new SomeDto("bla2", "blabla");
// condition 3
if (someDto.getName3() == null || checkSurName(anotherDto.getName()))
return new SomeDto("bla3", "blabla");
// condition 4
if (someDto.getName4() == null && checkSurName(anotherDto.getName()))
return new SomeDto("bla4", "blabla");
// condition 5
if (someDto.getName5() == null || checkSurName(anotherDto.getName()))
return new SomeDto("bla5", "blabla");
// condition 6
if …Run Code Online (Sandbox Code Playgroud) java graph-theory cyclomatic-complexity checkstyle sonarqube
我使用 Visual Studio Code Metrics 做了一些测试。由于我可以计算圈复杂度,因此每个if, while, for- 运算符都会将复杂度增加 1。我有下一个简单的方法:
static bool ContainsNegative(int a, int b, int c, int d)
{
if (a < 0 || b < 0 || c < 0 || d < 0) return false;
return true;
}
Run Code Online (Sandbox Code Playgroud)
但对于它来说,圈复杂度是 5,而不是 2(1 表示方法 + 1 表示if)。我的问题是 - 这是因为代码指标将if运算符中的每个条件计算为不同的if?即我的方法相当于:
static bool ContainsNegative(int a, int b, int c, int d)
{
if (a < 0) return false;
if (b …Run Code Online (Sandbox Code Playgroud) 假设我有这个方法:
void DoStuff(int a)
{
int b;
switch (a)
{
case 1:
b = 5;
break;
case 2:
b = 100;
break;
default:
b = 0;
break;
}
Console.WriteLine(b);
}
Run Code Online (Sandbox Code Playgroud)
Visual Studio 给出的圈复杂度为 4(在我看来,实际的圈复杂度是 3,但这不是重点)。
现在,如果我使用 switch 表达式重构代码,我会得到:
void DoStuff(int a)
{
int b = a switch
{
1 => 5,
2 => 100,
_ => 0
};
Console.WriteLine(b);
}
Run Code Online (Sandbox Code Playgroud)
Visual Studio 现在表示圈复杂度为 1,尽管代码正在执行完全相同的操作。圈复杂度真的降低了吗?如果是这样,怎么办?
我正在大学的软件质量保证课程中学习Cyclomatic Complexity,当你在谓词语句或节点中有复合条件时,我很难理解它是如何工作的.我已经看到了Cyclomatic Complexity的多个定义,其主要用于我的课堂
V(G) = # of predicate nodes (with outdegree = 2) + 1
Run Code Online (Sandbox Code Playgroud)
因此,对于像这样的程序图,我们将具有两个圈复数:
V(G) = 2
Run Code Online (Sandbox Code Playgroud)

我也看到了Cyclomatic复杂性的定义
V(G) = # edges - # nodes + 2
Run Code Online (Sandbox Code Playgroud)
这也适用于上面给出的图表.但是,我们有谓词节点的复合条件.我们认为语言和系统设置为自动短路,在我看来左下方的图表必须扩展到图表.如果是这种情况,如下所示,Cyclomatic复杂度是否变为3,即使在真实的源代码中,我们可能只有一个if语句或者它仍然是V(G)= 2?.
这是令人困惑的,因为我看到的Cyclomatic复杂性的大多数定义都在讨论谓词节点,而且我理解谓词它们可能包含多个短路条件.如果不是这种情况,似乎短路行为在提高性能的同时,实际上会在考虑时提高Cyclomatic的复杂性.如果给出了源代码,那么在我们计算出圈复杂度之前,是否必须将所有条件分解到它们自己的节点中,例如左边的图形?

testing unit-testing graph-theory graph cyclomatic-complexity
我试图了解圈复杂度的工作原理以及如何避免警告。是的,我明白编写代码的目的不是避免任意警告,但我至少想知道发生了什么,以便我可以决定我看到的代码是好还是坏。
我有一个看起来像的函数:
protected function update($uuid, $data, $householdUuid, $androidId) {
$household = $this->householdService->getHouseholdByUuid($householdUuid);
$this->updatePeriod($household, $data);
$this->updateNickname($household, $data, $androidId);
$this->updateDateOrder($household, $data);
$this->updateCurrency($household, $data);
$this->updateAccounts($household, $data);
$household->save();
return $this->respondUpdated();
}
Run Code Online (Sandbox Code Playgroud)
这被标记为具有 10 的圈复杂度。这怎么可能?从文档中,我将其视为 1。唯一的可能性是 PHPMD 正在下降到各种方法调用中。
但如果是这样,那么我就没有办法“修复”这个方法了。通常,我会通过提取较小的辅助方法来降低方法的复杂性。该方法已经被重构为各种 update() 方法,以消除发生的一堆条件更新。原始方法的圈复杂度也为 10,并且重构没有做任何事情。
或者问题可能更简单——我通过与 Jenkins 设置的持续集成来运行 PHPMD。PHPMD 没有使用最新的代码会不会有问题?在我已经将类重构到行数限制以下之后,我遇到了一些类似的问题,它会将类标记为行数过多。
在一个使用 spring reactor 的项目中,我们检测到我们的静态分析工具没有检测到反应代码的真正复杂性。目前我们正在使用 PMD、Checkstyle、Spotbugs 和 SonarQube 的组合,但它们都无法检测“复杂的反应代码”。
有没有其他工具可以考虑到 Java 中新的响应式 API 和流 API?
一些被认为具有圈复杂度 0 的非常简单的例子可能是:
// EXAMPLE 1: Optional usage for avoiding null field checks
int activeSensors = Optional.of(sensorData).map(SensorData::getActiveCount).orElse(0);
// EXAMPLE 2: Stream API usage for replacing iterations and conditionals
list.stream().filter(Objects::nonNull).forEach(item -> do_something(item));
// EXAMPLE 3: Reactive API
Flux<Float> historyValuesFlux = Mono.justOrEmpty(sensorData)
.filter(Objects::nonNull)
.flatMapMany(data -> Flux.fromIterable(data.getHistoryPoints()))
.filter(Objects::nonNull)
.map(SensorHistoryPoint::getValue);
Run Code Online (Sandbox Code Playgroud)
在这三种情况下,“经典等效”代码具有更高的复杂度值,因此我们担心代码的实际复杂度被新语法隐藏了。这意味着我们对代码可维护性的估计是错误的,并且会随着时间的推移而恶化。
java static-analysis cyclomatic-complexity reactive-programming java-stream
c# ×4
java ×4
graph-theory ×2
sonarqube ×2
c#-8.0 ×1
checkstyle ×1
code-metrics ×1
codemaid ×1
graph ×1
java-stream ×1
jenkins ×1
lambda ×1
ndepend ×1
php ×1
phpmd ×1
testing ×1
unit-testing ×1