Как провести рефакторинг этой функции, чтобы снизить ее когнитивную сложность с 17 до 15 разрешенных - PullRequest
2 голосов
/ 13 июля 2020
  • Как провести рефакторинг этой функции, чтобы уменьшить сложность
  • Когда я использую case case в то время, сложность кода больше, чем как ее уменьшить

    How to достичь этого


var has = Object.prototype.hasOwnProperty



var toString = Object.prototype.toString


function isEmpty(val) {
 
  if (val == null) return true
  if ('boolean' == typeof val) return false
  if ('number' == typeof val) return val === 0
  if ('string' == typeof val) return val.length === 0
  if ('function' == typeof val) return val.length === 0
  if (Array.isArray(val)) return val.length === 0
  if (val instanceof Error) return val.message === ''
  if (val.toString == toString) {
    switch (val.toString()) {
      case '[object File]':
      case '[object Map]':
      case '[object Set]': {
        return val.size === 0
      }
      case '[object Object]': {
        for (var key in val) {
          if (has.call(val, key)) return false
        }

        return true
      }
    }
  }
  return false
}
module.exports = isEmpty

Ответы [ 2 ]

0 голосов
/ 20 июля 2020

Недавно я дал ответ на очень похожий вопрос, более подробно рассказывая о том, как работает когнитивная сложность (см. { ссылка }).

Но в целом я думаю, что это Важно понимать, что когнитивная сложность увеличивается еще больше, если есть вложенные условия . Этот расчет выполняется таким образом, потому что человеческий мозг может намного лучше справляться с операторами, написанными последовательно, а не с вложенными условиями. Таким образом, для каждого условного оператора (if, switch, для l oop, et c.) К значению сложности будет добавлено +1. Но для каждого вложенного условия добавляется еще +1 поверх последнего уровня. Это означает, что if внутри if добавит не только +1, но и +2. Если внутри if, внутри if приведет к +1 для первого условия if, +2 для второго и +3 для третьего условия if. Если вы хотите углубиться в это, я рекомендую взглянуть на: https://www.sonarsource.com/docs/CognitiveComplexity.pdf

Итак, давайте сначала проанализируем, откуда берутся значения высокой сложности из вашего метода:

function isEmpty(val) {
    if (val == null) return true // +1 
    if ('boolean' == typeof val) return false // +1
    if ('number' == typeof val) return val === 0 // +1
    if ('string' == typeof val) return val.length === 0 // +1
    if ('function' == typeof val) return val.length === 0 // +1
    if (Array.isArray(val)) return val.length === 0 // +1
    if (val instanceof Error) return val.message === '' // +1
    if (val.toString == toString) { // +1
        switch (val.toString()) { // +2
            case '[object File]':
            case '[object Map]':
            case '[object Set]': {
                return val.size === 0
            }
            case '[object Object]': {
                for (var key in val) { // +3
                    if (has.call(val, key)) return false // +4
                }

                return true
            }
        }
    }
    return false
}

Если вы посмотрите на добавленные мною комментарии, вы легко увидите, где находится наиболее проблемный c код, касающийся сложности c cyclomati. Это также относится к удобочитаемости кода для человека.

Таким образом, один простой шаг к повышению читабельности и в то же время снижению общей сложности - это поиск вариантов " ранний возврат ".

Чтобы проиллюстрировать это, я просто инвертировал оператор * if (val.toString == toString) ", чтобы немедленно вернуть false if * val .toString! = toString ":

function isEmpty(val) {
    if (val == null) return true // +1 
    if ('boolean' == typeof val) return false // +1
    if ('number' == typeof val) return val === 0 // +1
    if ('string' == typeof val) return val.length === 0 // +1
    if ('function' == typeof val) return val.length === 0 // +1
    if (Array.isArray(val)) return val.length === 0 // +1
    if (val instanceof Error) return val.message === '' // +1
    if (val.toString != toString) { // +1
        return false;
    }
    
    switch (val.toString()) { // +1
        case '[object File]':
        case '[object Map]':
        case '[object Set]': {
            return val.size === 0
        }
        case '[object Object]': {
            for (var key in val) { // +2
                if (has.call(val, key)) return false // +3
            }
            return true
        }
    }
}  

Теперь последний оператор switch может выполняться вне оператора if, и мы снизили уровень вложенности на единицу. С этим простым изменением когнитивная сложность теперь упала до 14 вместо 17.

Вы могли бы даже go сделать шаг дальше и изменить последний оператор case путем извлечения возвращаемого значения в переменную и извлечения отдельного метода из блока кода. Это снизит сложность метода isEmpty () еще больше.

И помимо извлечения метода вы также можете использовать декларативный подход и использовать, например, метод Array find () что уменьшило бы когнитивную сложность еще больше.

Чтобы проиллюстрировать идею, я сделал и то, и другое:

function isEmpty(val) {
    if (val == null) return true // +1 
    if ('boolean' == typeof val) return false // +1
    if ('number' == typeof val) return val === 0 // +1
    if ('string' == typeof val) return val.length === 0 // +1
    if ('function' == typeof val) return val.length === 0 // +1
    if (Array.isArray(val)) return val.length === 0 // +1
    if (val instanceof Error) return val.message === '' // +1
    if (val.toString != toString) { // +1
        return false;
    }
    
    return checkForComplexTypes(val)
}

function checkForComplexTypes(val) {
    var result = null
    switch (val.toString()) { // +1
        case '[object File]':
        case '[object Map]':
        case '[object Set]': {
            result = val.size === 0
        }
        case '[object Object]': {
            result = Object.keys(val).find(key => has.call(val, key))
        }
        return result
    }
}

Это должно снизить когнитивную сложность из isEmpty () метод до 8 и весь код , включая извлеченный checkForComplexTypes () функцию до оценки сложности 9 .

Примечание: JavaScript не является моим основным языком на данный момент, поэтому я не могу полностью гарантировать правильность последнего шага рефакторинга.

0 голосов
/ 13 июля 2020

Если вы не можете разделить функцию или использовать подход OOP, вы можете использовать массив функций и перебирать его:

const has = Object.prototype.hasOwnProperty;
const toString = Object.prototype.toString;

function isEmpty(val) {
    let isEmpty = null;

    const checkFunctions = [
        (val) => 'boolean' === typeof val ? false : null,
        (val) => 'number' === typeof val ? val === 0 : null,
        (val) => ['string', 'function'].includes(typeof val) ? val.length === 0 : null,
        (val) => Array.isArray(val) ? val.length === 0 : null,

        (val) => val instanceof Error ? val.message === '' : null,

        (val) => val.toString == toString && ['[object File]', '[object Map]', '[object Set]'].includes(val.toString()) ? val.size === 0 : null,
        (val) => {
            if (val.toString == toString && val.toString() === '[object Object]') {
                for (var key in val) {
                    if (has.call(val, key)) return false
                }
                return true;
            }
        }
    ];

    for (let i = 0; i < checkFunctions.length; i++) {
        isEmpty = checkFunctions[i](val);
        if (isEmpty !== null) {
            return isEmpty;
        };
    }
}

console.log(isEmpty(''), true);
console.log(isEmpty('Hallo'), false);
console.log(isEmpty(0), true);
console.log(isEmpty(1), false);
console.log(isEmpty({}), true);
console.log(isEmpty({a: 1}), false);

Вы также можете расширить основные типы до JS, а затем вместо isEmpty (val) написать val.isEmpty (). Например:

String.prototype.isEmpty = function() {return this.length === 0}
Array.prototype.isEmpty = function() {return this.length === 0}

console.log("".isEmpty(), true);
console.log("foo".isEmpty(), false);
console.log([].isEmpty(), true);
console.log([1,2,3].isEmpty(), false);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...