Советы по уменьшению размеров ваших функций

Использование строк кода (LoC) для измерения продуктивности разработчиков - это хорошо известный плохой показатель; примерно эквивалентно измерению успеха в постройке самолетов по весу произведенных самолетов.

Однако изучение LoC в базе кода может быть мощным средством решения проблем технической задолженности и проблем сложности при рассмотрении одного файла, класса, модуля или функции.

Длинные функции или методы обычно нарушают надлежащие методы кодирования, вызывая следующие проблемы

  • Они обладают слишком большой функциональностью в заданном объеме и увеличивают когнитивную нагрузку, необходимую человеку, читающему код.
  • Длинные функции затрудняют эффективное повторное использование кода
  • Эффективное тестирование может быть затруднено, поскольку отдельные части функции трудно изолировать.
  • Их становится все труднее изменять, не внося ошибок.
  • Отсутствие эффективного управления общим состоянием, поскольку многие несвязанные переменные могут быть изменены в одной области

В этой короткой статье я хочу рассказать о некоторых основных советах и ​​методах, которые вы можете применить к существующим кодовым базам для рефакторинга длинных функций. Что еще более важно, вы можете применять их во время написания кода, в первую очередь предотвращая накопление технического долга.

Как и финансовый долг, технический долг дешевле всего погасить ближе всего к моменту создания, и иногда он может быть почти бесплатным, если его вообще не создавать.

Извлечь функцию

Это, безусловно, самый простой способ уменьшить размер ваших функций и может показаться почти тривиальным, но он дает другие преимущества, которые вы можете не учитывать изначально.

При добавлении части функциональности, а не добавлении кода в конец текущей функции, почему бы не создать новую функцию с этой добавленной функциональностью, описательным именем и вызвать ее из функции, которая изначально должна была получить код.

Скажем, у нас есть следующая простая функция для поиска области прямоугольника

function area(rectangle) {
  return rectangle.width * rectangle.height;
}

Если затем нас попросят добавить что-то вроде простого ведения журнала, мы могли бы сделать это

function area(rectangle) {
  console.log("Width: " + rectangle.width);
  console.log("Height: " + rectangle.height);
  return rectangle.width * rectangle.height;
}

Вероятно, это нормально, так как у нас все еще меньше нескольких строк, но для этого мы могли бы вместо этого сделать это

function area(rectangle) {
  logDimensions(rectangle);
  return rectangle.width * rectangle.height;
}
function logDimensions(shape) {
  console.log("Width: " + shape.width);
  console.log("Height: " + shape.height);
}

Общее количество линий увеличилось, но наши функции остались небольшими, что и было нашей целью. Также вы могли заметить, что мы создали функцию многократного использования для регистрации измерений, которая может быть полезна для других функций, которые мы могли бы реализовать.

Что наиболее важно для меня, хотя мы упростили чтение функции площади, я могу быстро решить, интересует ли меня часть кода, в которой ведется журнал, в противном случае я могу пропустить одну строку, а не пытаться интерпретировать, что это за код пытаюсь выполнить.

Допустим, у нас была следующая наивная функция переключения светофора с красного на зеленый.

Давайте посмотрим, как мы можем использовать это для существующего фрагмента кода.

function changeRedToGreen() {
  // Reset all lights
  setLight(RED_LIGHT, false);
  setLight(YELLOW_LIGHT, false);
  setLight(GREEN_LIGHT, false);
  
  // Set to Red + Yellow
  setLight(RED_LIGHT, true);
  setLight(YELLOW_LIGHT, true);
  
  // Start timer to set Green
  setTimeout(function() {
    setLight(RED_LIGHT, false);
    setLight(YELLOW_LIGHT, false);
    setLight(GREEN_LIGHT, true);  
  }, 4000);
}

Извлекая некоторые методы, мы могли бы уменьшить это до

function changeRedToGreen() {
  resetLights();
  setStandbyLights(true);
  
  setTimeout(function() {
    setStandbyLights(false);
    setLight(GREEN_LIGHT, true);
  }, 4000);
}
function resetLights() {
  setLight(RED_LIGHT, false);
  setLight(YELLOW_LIGHT, false);
  setLight(GREEN_LIGHT, false);
}
function setStandbyLights(state) {
  setLight(RED_LIGHT, state);
  setLight(YELLOW_LIGHT, state);
}

Мы начали с 14 строк (17 с комментариями) и закончили с 20 строками. Однако наша самая длинная функция увеличилась с 12 до 7, что является очень хорошим сокращением.

Вы также могли заметить, что я удалил комментарии после того, как выбрал описательные имена функций.

Извлечение анонимной функции

Многие современные языки также допускают анонимные встроенные функции, как мы видели в примере со светофором выше.

setTimeout(function() {
  setStandbyLights(false);
  setLight(GREEN_LIGHT, true);
}, 4000);

Хотя очень полезно; Я обнаружил, что они часто являются основной причиной долгих трудностей для чтения функций, поскольку мы скрываем действия и концепции, которые можно было бы лучше назвать и выделить.

Давайте сделаем этот небольшой рефакторинг для нашего кода светофора сверху.

function changeRedToGreen() {
  resetLights();
  setStandbyLights(true);
  
  setTimeout(setOnlyGreen, 4000);
}
function setOnlyGreen() {
  setStandbyLights(false);
  setLight(GREEN_LIGHT, true);
}
...

В этом сценарии мы увеличили общее количество строк только на 1, но наша самая длинная функция теперь составляет всего 3 строки.

Передача именованной функции вместо анонимной встроенной функции делает намерение явным и означает, что нам не нужно анализировать содержимое, чтобы определить, интересует оно нас или нет.

Моя функция слишком долгая?

Как долго это слишком долго? Это зависит от ряда факторов, начиная от языка, на котором вы работаете, и заканчивая сферой бизнеса и предпочтениями команды.

Лично для меня при работе с современными выразительными языками программирования около 5 строк - это хорошая длина, которая легко достижима.

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

  • Использование комментариев для описания отдельных частей функции, таких как исходный пример освещения выше.
  • Длинные вложенные блоки кода, например несколько строк кода в каждой ветви условного
  • Глубокая вложенность или встроенные функции, как правило, намекают на концепцию, которую можно назвать отдельно.
  • Множественные обязанности: если бы вы описали, что делает функция, и использовали слово «и», скорее всего, она будет делать слишком много вещей.
  • Четкое разделение нескольких сегментов в функции пробелом

С помощью этих двух очень простых извлечений вы сможете не только сделать свои функции короткими, но и сделать свой код более выразительным и более понятным.

Что мне действительно нравится в этих методах, так это то, что вы должны иметь возможность быстро применять оба из них к новому коду, чтобы предотвратить использование длинных функций в первую очередь, но также и к существующему коду, не проводя обширного рефакторинга или реструктуризации вашего кода.

Наконец, пусть вас не обманывает простота, такие небольшие улучшения, как эти, лежат в основе высококачественного кода.

Я хотел бы услышать о вашем опыте использования кратких функций, я планирую следующую часть с более продвинутыми техниками, поэтому, если у вас есть методика или совет, которыми вы можете поделиться, свяжитесь с нами.

Благодарим за прочтение и щелкните значок 👏 ниже, если вам понравился этот пост, и если качество кода вас интересует, подпишитесь на меня здесь и в Twitter @efexen Если вы еще этого не сделали, могу порекомендовать проверить некоторые из моих других сообщений и дайте мне знать, что вы думаете 👍