Почему это имеет значение ? Как сделать ?

Рефакторинг 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

Github: https://github.com/helabenkhalfallah