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

问题描述 投票:0回答:4

如何降低给定代码段的复杂性?我在 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 {
javascript angular sonarqube karma-jasmine sonarlint
4个回答
79
投票

有关认知复杂性如何运作以及为何应保持较低认知复杂性的一些信息

首先,了解“认知复杂性”与“环路复杂性”相比如何工作非常重要。认知复杂性考虑了人脑感知的复杂性。这就是为什么它不简单地指示条件路径的数量(简化了返回语句的条件数量加1)。

“认知复杂性”指标还考虑嵌套条件(例如,if 内的 if、if 语句内的 if),这使得从人类的角度阅读和理解代码变得更加困难。 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 }

通过简单的 extract 方法重构
大量重复

(参见 getInfoItem() 函数)也被消除了,这可以轻松降低复杂性并增加可读性 老实说,我什至会更进一步,进一步重构您的代码,以便在提供设备详细信息时检查空项目和设置默认值(此处为空字符串)的逻辑由设备类或设备详细信息类本身具有更好的数据内聚性和对该数据进行操作的逻辑。但由于我不知道代码的其余部分,这一初始重构应该会让您更进一步,获得更好的可读性和更低的复杂性。

注意:我们甚至可以更进一步,执行所谓的 Replace Nested Conditional with Guard Clauses 重构(有时也称为“early return”或“invert if statements”) . 这可能会导致如下所示的代码,并且由于

消除了 else 语句,进一步将认知复杂度降低 1,最终的认知复杂度为 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);



2
投票

device.deviceManager && device.deviceManager.managerId ||空
  • 会变成

device.deviceManager?.managerId ||空

1
投票
this.deviceDetails = {...

转移到其自己的映射函数以获得快速解决方案。

    


0
投票

© www.soinside.com 2019 - 2024. All rights reserved.