Я полагаю, что многие студенты столкнулись с бизнес-логикой, запутавшейся глубоко в if else при поддержке старых проектов. Перед лицом такого беспорядка простое и грубое продолжение внесения постепенных изменений часто только увеличивает сложность и ухудшает читабельность. Вот три простых и общих метода рефакторинга.
что такое код лапши
Так называемый «спагетти-код» обычно используется при обработке сложных бизнес-процессов. Обычно он удовлетворяет следующим характеристикам:
- длинное содержание
- нарушение структуры
- Глубоко вложенный
Мы знаем, что основные языки программирования имеют функции или методы для организации кода. Для кода спагетти думайте об этом как о функции, которая удовлетворяет этим характеристикам. Согласно различию языковой семантики, его можно разделить на два основных типа:
if...if
тип
Этот тип структуры кода выглядит следующим образом:
function demo (a, b, c) {
if (f(a, b, c)) {
if (g(a, b, c)) {
// ...
}
// ...
if (h(a, b, c)) {
// ...
}
}
if (j(a, b, c)) {
// ...
}
if (k(a, b, c)) {
// ...
}
}
Блок-схема выглядит следующим образом:
Он вложен сверху внизif
, чтобы поток управления внутри одной функции продолжал расти.Не думайте, что сложность возрастает только линейно по мере роста потока управления.. Мы знаем, что функции имеют дело с данными, и каждаяif
В общем, будет логика обработки данных. Тогда даже при отсутствии вложенности, если таких абзацев 3if
, то по каждомуif
Выполнять или нет, существует 2 ^ 3 = 8 состояний данных. Если сегментов 6, то 2^6 = 64 состояния. В результате при увеличении масштаба проекта возрастает сложность отладки функции.экспоненциальныйрост! Это по порядку величин соответствует опыту «Мифа о человеко-месяце».
else if...else if
тип
Этот тип потока управления кодом также очень распространен. В форме:
function demo (a, b, c) {
if (f(a, b, c)) {
if (g(a, b, c)) {
// ...
}
// ...
else if (h(a, b, c)) {
// ...
}
// ...
} else if (j(a, b, c)) {
// ...
} else if (k(a, b, c)) {
// ...
}
}
Блок-схема выглядит следующим образом:
else if
В конце концов, будет введена только одна из ветвей, поэтому вышеуказанного взрыва комбинации не произойдет. Однако сложность также не является низкой при глубокой вложенности. Предполагая 3 уровня вложенности, каждый уровень имеет 3else if
, то выходов будет 3^3=27. Если каждый выход соответствует способу обработки данных, то инкапсуляция такого количества логики в функцию явно нарушает принцип единой ответственности. Кроме того, два вышеуказанных типа можно легко комбинировать, что еще больше увеличивает сложность и снижает читабельность.
Но почему в эпоху различных продвинутых фреймворков и библиотек классов такой код все еще появляется так часто? Моя личная точка зрения заключается в том, что повторно используемые модули действительно могут позволить нам писать меньше [кода шаблона], но независимо от того, как инкапсулирован сам бизнес, разработчикам все равно нужно писать логику. И даже простойif else
, что также позволяет усложнить поток управленияэкспоненциальныйрост. С этой точки зрения,Если у вас нет базовой грамотности в программировании, как бы быстро вы ни освоили лучшие фреймворки и библиотеки классов, в проекте будет бардак..
стратегия рефакторинга
Выше мы обсудили два типа спагетти-кода и количественно продемонстрировали, как они позволяют экспоненциально увеличивать сложность потока управления. Однако в современных языках программирования эта сложность полностью управляема. Ниже приведен список методов программирования для улучшения спагетти-кода в нескольких случаях.
базовый вариант
Для тех, у которых, кажется, самая быстрорастущая сложностьif...if
Введите код лапши, который можно разделить на основные функции. Каждое зеленое поле на изображении ниже представляет новое разделение функций:
Поскольку современные языки программирования отказалисьgoto
, поэтому неважно, насколько сложен поток управления, порядок выполнения кода в теле функции — сверху вниз. Поэтому мы вполне способныБез изменения логики потока управления, разбить одну большую функцию сверху вниз и постепенно разделить ее на несколько небольших функций, а затем вызывать их одну за другой. Этим навыком часто пользуются опытные студенты, и конкретная реализация кода здесь повторяться не будет.
Следует отметить, что так называемаяНе изменяет логику потока управления, что означает, что изменение не должно менять способ выполнения бизнес-логики, просто [переместите код и поместите его в функциональный слой]. Некоторые студенты могут подумать, что этот метод может вылечить симптомы, но не первопричину, но это всего лишь разрезание большого куска лапши на несколько маленьких кусочков, и в этом нет существенной разницы.
Но так ли это на самом деле? Таким образом, мы смогли разделить большую функцию с 64 состояниями на 6 меньших функций, которые возвращают только 2 разных состояния, и основную функцию, которая вызывает их одно за другим. Таким образом,Скорость роста сложности каждой функции снижается с экспоненциального уровня до линейного уровня.
С этим мы решилиif...if
Введите код лапши вверх, затем дляelse if...else if
какой тип?
Справочная таблица
заelse if...else if
типа спагетти-кода, одна из самых простых стратегий рефакторинга — использование так называемых интерполяционных таблиц. Он инкапсулирует каждыйelse if
Логика в:
const rules = {
x: function (a, b, c) { /* ... */ },
y: function (a, b, c) { /* ... */ },
z: function (a, b, c) { /* ... */ }
}
function demo (a, b, c) {
const action = determineAction(a, b, c)
return rules[action](a, b, c)
}
каждыйelse if
Логика в переписывается как отдельная функция, тогда мы можем разделить процесс следующим образом:
Для языков сценариев, которые по своей сути поддерживают отражение, это тривиальная техника. Но для более сложныхelse if
условие, этот метод переориентирует сложность потока управления, чтобы решить проблему [какую ветвь выбрать]determineAction
середина. Есть ли лучший способ справиться с этим?
Схема цепочки ответственности
В приведенном выше примере таблица поиска реализована с помощью пар ключ-значение, и для каждой ветвиelse if (x === 'foo')
В таком простом суждении'foo'
Его можно использовать в качестве ключа рефакторинговой коллекции. но если каждыйelse if
ветвьСодержит сложные условные суждения и требует порядка исполнения, то мы можем использовать шаблон цепочки ответственности для лучшего рефакторинга такой логики.
правильноelse if
С точки зрения, обратите внимание, что каждая ветвь на самом делеСудя сверху вниз и, наконец, введя только один из нихиз. Это означает, что мы можем добиться такого поведения, сохранив массив [правил суждения]. Если правило совпадает, выполняется ветвь, соответствующая этому правилу. Такой массив мы называем [цепочка ответственности], а поток выполнения в этом режиме следующий:
С точки зрения реализации кода мы можем определить иelse if
Совершенно эквивалентные правила:
const rules = [
{
match: function (a, b, c) { /* ... */ },
action: function (a, b, c) { /* ... */ }
},
{
match: function (a, b, c) { /* ... */ },
action: function (a, b, c) { /* ... */ }
},
{
match: function (a, b, c) { /* ... */ },
action: function (a, b, c) { /* ... */ }
}
// ...
]
rules
У каждого изmatch
а такжеaction
Атрибуты. В это время мы можем преобразовать исходную функциюelse if
Перепишем обход массива цепочки ответственности:
function demo (a, b, c) {
for (let i = 0; i < rules.length; i++) {
if (rules[i].match(a, b, c)) {
return rules[i].action(a, b, c)
}
}
}
В настоящее время, как только каждая ответственность будет сопоставлена, исходная функция вернется напрямую, что также полностью соответствуетelse if
семантика. Таким образом, мы достигаем комплексногоelse if
логически разделены.
Суммировать
На самом деле спагетти-код легко появляется в стиле «грубой и быстрой» разработки, не задумываясь. Много простых и грубых [добавьте сюдаif
, где несколькоreturn
] метод исправления ошибок вкупе с отсутствием комментариев легко ухудшить читаемость кода и усложнить его.
Но несколько решений этой проблемы не сложны. Причина, по которой эти примеры просты, заключается в том, что мощная выразительная сила языков программирования высокого уровня смогла обеспечить прямую семантическую поддержку требований, не полагаясь на коды шаблонов различных шаблонов, без необходимости применять восьмикомпонентный код. текст различных шаблонов дизайна.
Конечно, вы можете использовать шаблоны для обобщения некоторых трюков, чтобы уменьшить сложность бизнес-логики, но если вы будете запоминать и использовать шаблоны наизусть, вы также можете сбиться с пути в чрезмерном проектировании. При реализации общих бизнес-функций освоение языка программирования, разбор требований и реализация их простейшим кодом — уже оптимальное решение.