Почему это имеет значение ? Как сделать ?
Рефакторинг Genesis
Вдохновение
Если вы хотите, чтобы все было идеально, смотрите на природу.
От вселенной к организации человеческого тела и внутренней связи с атомами. Все построено вокруг: модульности, композиции, единоличной ответственности, возможности повторного использования, взаимодополняемости, независимости и самокоррекции.
У нас не может быть дублированного атома водорода с той же ролью и такими же химическими характеристиками. Составные молекулы являются результатом состава небольших атомов. Человеческие болезни иногда обнаруживаются и исправляются самим организмом.
То же самое и с нашими программами и алгоритмами.
Написание хорошего кода - не исключение, а нативный. Однако плохой код - это плохая мутация.
Тело человека
Человеческое тело состоит из независимых частей, которые взаимодействуют друг с другом, чтобы одновременно выполнять определенную и глобальную роль (модульность и состав).
Части слегка связаны и иметь единственную и определенную роль: без повторений, без двойной ответственности, без общего состояния, разделения, изоляции, все ясно и организовано (СУХОЕ, единственная ответственность, разделение проблемы,…).
Отсутствие общего состояния, единственная ответственность, разделение задач, состав и модульность не приводят к параллелизации и высокому уровню производительности.
Каждая часть абстрагирует свой рабочий режим и предоставляет входы и выходы для связи с внешним миром (компоненты черного ящика).
Вот почему в медицине мы можем заменить один орган другим, если он совместим.
Слабая связь внутри человеческого тела означает общение с использованием нейронной сети, чтобы сообщить «уникальному менеджеру и уникальному источнику истины» текущее состояние, чтобы выполнять соответствующие действия и поддерживать синхронизацию частей: мозг.
Детали не взаимодействуют друг с другом напрямую, но с помощью нейронной сети, и это важно для простых компонентов plug & play (если части взаимодействуют друг с другом, совместимость будет трудно найти) .
Мозгу не нужно знать, как сердце работает изнутри, но он должен быть информирован (распространение сигналов), если сердце сталкивается с какой-либо проблемой, чтобы выполнять правильные действия (события и наблюдатель, самометрика, самодиагностика, самодиагностика -исправление).
Урок: почему рефакторинг имеет значение?
Как и человеческое тело, если мы хотим иметь надежный код (тело), наши программы:
- должен быть модульным (легко добавлять / удалять / заменять части)
- должен состоять из небольших независимых и изолированных частей (без общего состояния, слегка связанных, без побочных эффектов, без мутации)
- не должно быть ни повторения ролей, ни дублирования кода (DRY и Single Responsibility)
В противном случае нам следует провести рефакторинг для достижения этих целей.
Рефакторинг - это лекарства, лекарства и хирургические операции для безопасного и здорового применения: замена или удаление частей, переименование, удаление дубликатов,…
Если да, мы можем подумать о распараллеливании, переносимости и производительности.
Наличие хорошо спроектированного и структурированного кода является приоритетом для создания производительного, эффективного и оптимального кода.
Если вам нужно добавить функцию в программу, но код не имеет удобной структуры, сначала выполните рефакторинг программы, чтобы упростить добавление функции, а затем добавьте эту функцию. - Рефакторинг: улучшение дизайна существующего кода, Мартин Фаулер -
Проблемы рефакторинга
Рефакторинг (существительное): изменение внутренней структуры программного обеспечения, чтобы упростить понимание и удешевить модификацию без изменения его наблюдаемого поведения. - Рефакторинг: улучшение дизайна существующего кода, Мартин Фаулер -
- Сделайте программное обеспечение более понятным
- Удешевление модификации программного обеспечения
- Без изменения наблюдаемого поведения
Как мы можем достичь этой цели?
Прежде чем приступить к рефакторингу, убедитесь, что у вас есть надежный набор тестов. Эти тесты должны проходить самопроверку. - Рефакторинг: улучшение дизайна существующего кода, Мартин Фаулер -
Процесс рефакторинга
Если кто-то скажет, что его код был сломан в течение нескольких дней, пока он проводил рефакторинг, вы можете быть уверены, что он не выполнял рефакторинг. - Рефакторинг: улучшение дизайна существующего кода, Мартин Фаулер -
После внесения изменений я немедленно компилирую и проверяю, не сломал ли я что-нибудь. Это важная привычка - проверять ее после каждого рефакторинга, каким бы простым он ни был. Ошибки легко сделать - по крайней мере, я считаю их легкими.
Тестирование после каждого изменения означает, что когда я делаю ошибку, мне нужно учесть лишь небольшое изменение, чтобы обнаружить ошибку, что значительно упрощает ее поиск и исправление.
В этом суть процесса рефакторинга: небольшие изменения и тестирование после каждого изменения. Если я попытаюсь сделать слишком много, то из-за ошибки я попаду в сложный эпизод отладки, который может занять много времени. Небольшие изменения, обеспечивающие тесную обратную связь, являются ключом к тому, чтобы избежать этого беспорядка. - Рефакторинг: улучшение дизайна существующего кода, Мартин Фаулер -
Рефакторинг изменяет программы небольшими шагами, поэтому, если вы допустили ошибку, легко найти причину ошибки. - Рефакторинг: улучшение дизайна существующего кода, Мартин Фаулер -
В этом случае тесты пройдены, поэтому мой следующий шаг - зафиксировать изменение в моей локальной системе управления версиями. Я использую систему контроля версий, такую как git или mercurial, которая позволяет мне делать частные коммиты. Я совершаю фиксацию после каждого успешного рефакторинга, поэтому я могу легко вернуться в рабочее состояние, если я испорчу позже. Затем я превращаю изменения в более важные коммиты, прежде чем отправлять изменения в общий репозиторий. - Рефакторинг: улучшение дизайна существующего кода, Мартин Фаулер -
Когда не следует проводить рефакторинг?
Когда проще переписать код, чем реорганизовать его! - Рефакторинг: улучшение дизайна существующего кода, Мартин Фаулер -
Когда нужно проводить рефакторинг?
Признаки плохого кода
Как и в случае с человеческим телом, иногда приложение показывает некоторые плохие симптомы, которые говорят нам: эту часть следует отремонтировать.
Наиболее важные симптомы плохого кода:
- Загадочное имя
- Дублированный код
- Длинная функция
- Длинный список параметров
- Глобальные данные
- Изменяемые данные
- Расходящиеся изменения
- Особенность Зависть
- Повторные переключения
- Петли
- Ленивый элемент
- Спекулятивная общность
- Временное поле
- Цепочки сообщений
- Средний человек
- Большой класс
- Комментарии
Приемы рефакторинга
Функция извлечения
Исходная функция:
function printOwing(invoice) { let outstanding = 0; console.log("***********************"); console.log("**** Customer Owes ****"); console.log("***********************"); // calculate outstanding for (const o of invoice.orders) { outstanding += o.amount; } // record due date const today = Clock.today; invoice.dueDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 30); //print details console.log(`name: ${invoice.customer}`); console.log(`amount: ${outstanding}`); console.log(`due: ${invoice.dueDate.toLocaleDateString()}`); }
Шаг 1 :
function printOwing(invoice) { let outstanding = 0; printBanner(); // calculate outstanding for (const o of invoice.orders) { outstanding += o.amount; } // record due date const today = Clock.today; invoice.dueDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 30); //print details console.log(`name: ${invoice.customer}`); console.log(`amount: ${outstanding}`); console.log(`due: ${invoice.dueDate.toLocaleDateString()}`); } function printBanner() { console.log("***********************"); console.log("**** Customer Owes ****"); console.log("***********************"); }
Шаг 2 :
function printOwing(invoice) { let outstanding = 0; printBanner(); // calculate outstanding for (const o of invoice.orders) { outstanding += o.amount; } // record due date const today = Clock.today; invoice.dueDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 30); printDetails(invoice, outstanding); } function printDetails(invoice, outstanding) { console.log(`name: ${invoice.customer}`); console.log(`amount: ${outstanding}`); console.log(`due: ${invoice.dueDate.toLocaleDateString()}`); }
Шаг 3 :
function printOwing(invoice) { let outstanding = 0; printBanner(); // calculate outstanding for (const o of invoice.orders) { outstanding += o.amount; } recordDueDate(invoice); printDetails(invoice, outstanding); } function recordDueDate(invoice) { const today = Clock.today; invoice.dueDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 30); }
Шаг 4:
function printOwing(invoice) { printBanner(); // calculate outstanding let outstanding = 0; for (const o of invoice.orders) { outstanding += o.amount; } recordDueDate(invoice); printDetails(invoice, outstanding); }
Шаг 5:
function printOwing(invoice) { printBanner(); let outstanding = calculateOutstanding(invoice); recordDueDate(invoice); printDetails(invoice, outstanding); } function calculateOutstanding(invoice) { let outstanding = 0; for (const o of invoice.orders) { outstanding += o.amount; } return outstanding; }
Шаг 6:
function printOwing(invoice) { printBanner(); const outstanding = calculateOutstanding(invoice); recordDueDate(invoice); printDetails(invoice, outstanding); } function calculateOutstanding(invoice) { let result = 0; for (const o of invoice.orders) { result += o.amount; } return result; }
Встроенная функция
Исходная функция:
function getRating(driver) { return moreThanFiveLateDeliveries(driver) ? 2 : 1; } function moreThanFiveLateDeliveries(driver) { return driver.numberOfLateDeliveries > 5; }
Реорганизованная функция:
function getRating(driver) { return (driver.numberOfLateDeliveries > 5) ? 2 : 1; }
Извлечь переменную
От :
return order.quantity * order.itemPrice - Math.max(0, order.quantity - 500) * order.itemPrice * 0.05 + Math.min(order.quantity * order.itemPrice * 0.1, 100);
To :
const basePrice = order.quantity * order.itemPrice; const quantityDiscount = Math.max(0, order.quantity - 500) * order.itemPrice * 0.05; const shipping = Math.min(basePrice * 0.1, 100); return basePrice - quantityDiscount + shipping;
Встроенная переменная
От :
let basePrice = anOrder.basePrice; return (basePrice > 1000);
To :
return anOrder.basePrice > 1000;
Изменение декларации функции
- Функция переименования
- Добавить параметр
- Удалить параметр
- Изменить подпись
Переименовать переменную
Объединение функций в класс или модуль
Разделенная фаза
Подстановочный алгоритм
Убрать среднего человека
Заявления на слайде
Разделить петлю
От :
let averageAge = 0; let totalSalary = 0; for (const p of people) { averageAge += p.age; totalSalary += p.salary; } averageAge = averageAge / people.length;
To :
let totalSalary = 0; for (const p of people) { totalSalary += p.salary; } let averageAge = 0; for (const p of people) { averageAge += p.age; } averageAge = averageAge / people.length;
Заменить цикл конвейером
Удалить мертвый код
Разделить переменную
Заменить Magic Literal
Разобрать условное
От :
if (!aDate.isBefore(plan.summerStart) && !aDate.isAfter(plan.summerEnd)) charge = quantity * plan.summerRate; else charge = quantity * plan.regularRate + plan.regularServiceCharge;
To :
charge = summer() ? summerCharge() : regularCharge(); function summer() { return !aDate.isBefore(plan.summerStart) && !aDate.isAfter(plan.summerEnd); } function summerCharge() { return quantity * plan.summerRate; } function regularCharge() { return quantity * plan.regularRate + plan.regularServiceCharge; }
Консолидировать условное выражение
Заменить вложенные условные предложения защитными предложениями
От :
function getPayAmount() { let result; if (isDead) result = deadAmount(); else { if (isSeparated) result = separatedAmount(); else { if (isRetired) result = retiredAmount(); else result = normalPayAmount(); } } return result; }
To :
function getPayAmount() { if (isDead) return deadAmount(); if (isSeparated) return separatedAmount(); if (isRetired) return retiredAmount(); return normalPayAmount(); }
Сохранить объект целиком
Заменить исключение предварительной проверкой
Как поддерживать работоспособность приложения?
Автоматические механизмы:
- Стандартные правила оздоровления: линтер.
- Работоспособность кода: модульные тесты (всегда измеряйте и улучшайте покрытие кода).
- Состояние здоровья: тесты e2e.
Ручные механизмы:
- Проверка запроса на извлечение.
- Регулярная проверка кода (по крайней мере, одна глобальная проверка кода каждую неделю).
Вывод
Написание хорошего кода - не исключение, а нативный. Однако плохой код - это плохая мутация.
Рефакторинг - это лекарства, лекарства и хирургические операции для безопасного и здорового применения: замена или удаление частей, переименование, удаление дубликатов,…
В этой истории я делюсь тем, что узнал о рефакторинге из своего опыта и из прекрасной книги Мартина Фаулера «Улучшение дизайна существующего кода».
Я рассказал о наиболее важных неприятных запахах кода и о том, какие техники используются чаще всего для повышения работоспособности кода.
использованная литература
- Рефакторинг: улучшение дизайна существующего кода, Мартин Фаулер -
Лучшая книга для изучения чистого кода и принципов рефакторинга.
Спасибо, что прочитали мою историю.
Вы можете найти меня по адресу:
Twitter: https://twitter.com/b_k_hela