Яка мета рефакторингу у вашому конкретному випадку?
Припустімо, щоб миритись з моєю відповіддю, що всі ми (певною мірою) віримо в TDD (Test-Driven Development).
Якщо метою вашого рефакторингу є очищення існуючого коду без зміни існуючої поведінки, то написання тестів перед рефакторингом - це те, як ви переконаєтесь, що ви не змінили поведінку коду, якщо вам це вдасться, то тести будуть успішними як до, так і після ти рефактор.
Тести допоможуть вам переконатися, що ваша нова робота справді працює.
Тести, ймовірно, також виявлять випадки, коли оригінальний твір не працює.
Але як же насправді зробити якийсь значний рефакторинг, не впливаючи на поведінку певною мірою?
Ось короткий список кількох речей, які можуть статися під час рефакторингу:
- перейменувати змінну
- перейменувати функцію
- додати функцію
- функція видалення
- розділити функцію на дві або більше функцій
- об'єднати дві або більше функцій в одну функцію
- розділений клас
- комбінувати заняття
- перейменувати клас
Я хочу стверджувати, що кожна з перерахованих видів діяльності певним чином змінює поведінку .
І я буду стверджувати, що якщо ваш рефакторинг змінить поведінку, ваші тести все ще будуть такими, якими ви гарантуєте, що нічого не зламали.
Можливо, поведінка не змінюється на макрорівні, але сенс тестування одиниці полягає не в забезпеченні макроповедінки. Ось інтеграційне тестування. Сенс тестування одиниці полягає в тому, щоб не порушити окремі шматочки та шматочки, з яких ви створюєте ваш продукт. Ланцюг, найслабша ланка тощо.
Як щодо цього сценарію:
Припустимо, у вас є function bar()
function foo()
здійснює дзвінок на bar()
function flee()
також робить дзвінок функціонувати bar()
Просто для різноманітності, flam()
дзвонить доfoo()
Все працює чудово (мабуть, принаймні).
Ви рефактор ...
bar()
перейменовано на barista()
flee()
змінюється на дзвінок barista()
foo()
це НЕ змінено на викликbarista()
Очевидно, що ваші тести для обох foo()
і flam()
зараз не виходять з ладу.
Можливо, ви не зрозуміли, foo()
подзвонили bar()
в першу чергу. Ви , звичайно , не розуміли , що flam()
залежить від bar()
шляху foo()
.
Що б там не було. Справа в тому, що ваші тести розкриють щойно порушену поведінку обох foo()
і flam()
, поступово, під час вашої роботи по рефакторингу.
Випробування в кінцевому підсумку допомагають вам добре переробити.
Якщо у вас немає тестів.
Це трохи надуманий приклад. Є такі, хто стверджує, що якщо змінити bar()
перерви foo()
, то це foo()
було занадто складно, щоб почати з цього і його слід розбити. Але процедури здатні викликати інші процедури з причини і неможливо усунути всю складність, правда? Наша робота - керувати складністю розумно.
Розглянемо інший сценарій.
Ви будуєте будівлю.
Ви будуєте ліси, щоб забезпечити правильність будівлі будівлі.
Ліси допомагають вам побудувати шахту ліфта, серед іншого. Після цього ви зриваєте ліси, але вал ліфта залишається. Ви знищили "оригінальну роботу", знищивши риштування.
Аналогія є неглибокою, але справа в тому, що будувати інструменти, які допоможуть вам створити продукт, не чутно. Навіть якщо інструменти не є постійними, вони корисні (навіть необхідні). Теслярі роблять джиги весь час, іноді лише для однієї роботи. Потім вони розривають дротики на частини, інколи використовуючи деталі для створення інших джигів для інших робіт, іноді ні. Але це не робить джиги марними або марними зусиллями.