javascript - 重构此方法以将其认知复杂度从 21 降低到允许的 15。如何重构和降低复杂度

标签 javascript angular sonarqube karma-jasmine sonarlint

如何降低给定代码的复杂度?我在 Sonarqube 中遇到此错误---> 重构此方法以将其认知复杂度从 21 降低到允许的 15。

this.deviceDetails = this.data && {...this.data.deviceInfo} || {};
    if (this.data && this.data.deviceInfo) {
      this.getSessionInfo();
      // tslint:disable-next-line: no-shadowed-variable
      const { device, driver, ipAddress, port, active, connectionType } = this.data.deviceInfo;
      this.deviceDetails = {
        name: device.name || '',
        manufacturer: device.manufacturer || '',
        deviceType: device.deviceType || '',
        model: device.model || '',
        description: device.description || '',
        managerId: device.deviceManager && device.deviceManager.managerId || null,
        locationId: device.location && device.location.locationId || null,
        active: device.active,
        connectionType: connectionType || null,
        driver_id: driver && driver.driverId || null,
        ipAddress: ipAddress || '',
        port: String(port) || '',
        connectionStatus: active,
      };
      this.oldDeviceDetails = {...this.deviceDetails};
      this.deviceLocation = device.location && device.location.locationId || null;
    } else {

最佳答案

关于认知复杂性如何工作以及为什么应该保持较低水平的一些信息
首先,重要的是要了解“认知复杂性”与“圈复杂性”相比如何工作。认知复杂性考虑了人脑感知的复杂性。这就是为什么它不简单地表示条件路径的数量(简化了条件的数量加 1 用于 return 语句)。
认知复杂性公制 考虑嵌套条件 (例如 if 中的 if,if 语句中的 if),这使得从人类的 Angular 阅读和理解代码变得更加困难。
以下来自 SonarQube 文档 (https://www.sonarsource.com/docs/CognitiveComplexity.pdf) 的示例概述了我要解释的内容:

if (someVariableX > 3) { // +1
    if (someVariableY < 3) { // +2, nesting = 1
        if (someVariableZ === 8) { // +3, nesting = 2
            someResult = someVariableX + someVariableY - someVariableZ;
        }
    }
}
因此请注意,二元运算会增加复杂性,但嵌套条件甚至会为每个嵌套条件加 1 分。这里认知复杂度为 6,而圈复杂度仅为 4(每个条件一个,返回路径一个);
如果您使代码对人类更具可读性,例如通过从包含条件的行中提取方法,您可以获得更好的可读性和更低的复杂性。
尽管您提供的代码没有嵌套条件,但我认为首先了解圈和认知复杂度计算的工作原理以及为什么将其保持在低水平是一个好主意很重要。
[TL;DR] 一种可能的方法,可以将您的代码重构为一个不太复杂且可读性更好的版本
让我们首先看看注释中概述的代码的复杂性计算是如何完成的:
this.deviceDetails = this.data && {  ...this.data.deviceInfo } || {}; // +2
if (this.data && this.data.deviceInfo) { // +1 for the if condition, +1 for the binary operator
    this.getSessionInfo();

    const { device, driver, ipAddress, port, active, connectionType } =             
    this.data.deviceInfo;
    this.deviceDetails = {
        name: device.name || '', // +1 for the binary operator
        manufacturer: device.manufacturer || '', // +1 for the binary operator
        deviceType: device.deviceType || '', // +1 for the binary operator
        model: device.model || '', // +1 for the binary operator
        description: device.description || '', // +1 for the binary operator
        managerId: device.deviceManager && device.deviceManager.managerId || null, // +2 for the varying binary operators
        locationId: device.location && device.location.locationId || null, // +2 for the varying binary operator
        active: device.active,
        connectionType: connectionType || null, // +1 for the binary operator
        driver_id: driver && driver.driverId || null, // +2 for the varying binary operator
        ipAddress: ipAddress || '', // +1 for the binary operator
        port: String(port) || '', // +1 for the binary operator
        connectionStatus: active,
    };
    this.oldDeviceDetails = { ...this.deviceDetails };
    this.deviceLocation = device.location && device.location.locationId || null; // +2 for the varying binary operator
} else { // +1 for the else path 
    // some statement
}
因此,假设我的数学是正确的,这总结为 SonarQube 报告的 21 的认知复杂度。
以下代码示例显示了如何将您的代码重构为应该 的版本。将认知复杂度降低到 12 . (请注意,这只是手动计算。)
可以通过应用简单的重构来完成,例如 提取方法和/或移动方法(另见 Martin Fowler,https://refactoring.com/catalog/extractFunction.html)。
this.deviceDetails = getDeviceInfo();
if (deviceInfoAvailable()) { // +1 for the if statement
    this.getSessionInfo();
    // tslint:disable-next-line: no-shadowed-variable
    const { device, driver, ipAddress, port, active, connectionType } = this.data.deviceInfo;
    this.deviceDetails = {
        name: getInfoItem(device.name), 
        manufacturer: getInfoItem(device.manufacturer),
        deviceType: getInfoItem(device.deviceType),
        model: getInfoItem(device.model),
        description: getInfoItem(device.description), 
        managerId: getManagerId(device),
        locationId: getDeviceLocation(device),
        active: device.active,
        connectionType: getInfoItem(connectionType, null), 
        driver_id: getDriverId(driver), 
        ipAddress: getInfoItem(ipAddress), 
        port: getInfoItem(port), 
        connectionStatus: active,
    };
    this.oldDeviceDetails = { ...this.deviceDetails };
    this.deviceLocation = getDeviceLocation(device);
} else { // +1 for the else
    // some statement
}

function getDeviceInfo()
{
    return this.data && { ...this.data.deviceInfo } || {}; // +2
}

function getDriverId(driver) {
    return driver && driver.driverId || null; // +2 for the binary operators
}

function getDeviceLocation(device) {
    return device.location && device.location.locationId || null; // +2 for the binary operators
}

function getManagerId(device) {
    return device.deviceManager && device.deviceManager.managerId || null; // +2 for the binary operators
}

function deviceInfoAvailable() {
    return this.data && this.data.deviceInfo; // +1 for the binary operator
}

function getInfoItem(infoItem, defaultValue = '') {
    return infoItem || defaultValue; // +1 for the binary operator
}
使用简单的提取方法重构 大量重复 (见 getInfoItem() 函数)被淘汰了这也使得降低复杂性和增加可读性变得容易。
老实说,我什至会更进一步并重新构建您的代码,以便在提供设备详细信息时检查空项目和设置默认值(此处为空字符串)的逻辑由设备类或设备详细信息类本身具有更好的数据凝聚力以及对该数据进行操作的逻辑。但是由于我不知道其余的代码,所以这个初始重构应该让您更进一步,以提高可读性和降低复杂性。
备注 : 我们甚至可以更进一步,执行所谓的 Replace Nested Conditional with Guard Clauses重构(有时也称为“提前返回”或“反转 if 语句)。
这可能会导致下面显示的代码,并由于 将认知复杂度进一步降低一倍。消除 else 语句 ,导致最终的认知复杂度为 11 .提取的功能是相同的,因此这里不再列出......
this.deviceDetails = getDeviceInfo();
if (deviceInfoAvailable()) { // +1 for the if statement
    // some statement
    return; // return the same way as in the eliminated else clause
}

this.getSessionInfo();
const { device, driver, ipAddress, port, active, connectionType } = this.data.deviceInfo;
this.deviceDetails = {
  name: getInfoItem(device.name), 
  manufacturer: getInfoItem(device.manufacturer),
  deviceType: getInfoItem(device.deviceType),
  model: getInfoItem(device.model),
  description: getInfoItem(device.description), 
  managerId: getManagerId(device),
  locationId: getDeviceLocation(device),
  active: device.active,
  connectionType: getInfoItem(connectionType, null), 
  driver_id: getDriverId(driver), 
  ipAddress: getInfoItem(ipAddress), 
  port: getInfoItem(port), 
  connectionStatus: active,
};
this.oldDeviceDetails = { ...this.deviceDetails };
this.deviceLocation = getDeviceLocation(device);

关于javascript - 重构此方法以将其认知复杂度从 21 降低到允许的 15。如何重构和降低复杂度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62815733/

相关文章:

javascript - 为什么原型(prototype)考虑对象而不是函数

javascript - 我怎样才能使这个 "percentage-only"Regex 工作?

java - sonarQube 是否检测反射 api 调用的未使用方法

java - 使用 SonarQube Ant 任务进行分析

javascript - 为什么在 WebXR 中光线转换方向计算不正确?

javascript - 在 Angular 中将 *ngFor 与异步管道一起使用时出现无限循环

angular - Angular 14 中的引导安装失败

angular - 当 token 可以是多个值时,通过 Injector.get() 获取父组件

Angular -将字节数组显示为图像

gradle - SonarQube插件:如何从命令行-D获取属性值