Недавно я дал ответ на очень похожий вопрос, более подробно рассказывая о том, как работает когнитивная сложность (см. { ссылка }).
Но в целом я думаю, что это Важно понимать, что когнитивная сложность увеличивается еще больше, если есть вложенные условия . Этот расчет выполняется таким образом, потому что человеческий мозг может намного лучше справляться с операторами, написанными последовательно, а не с вложенными условиями. Таким образом, для каждого условного оператора (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 не является моим основным языком на данный момент, поэтому я не могу полностью гарантировать правильность последнего шага рефакторинга.