У мене є проект. У цьому проекті я хотів переробити його, щоб додати функцію, і я відновив проект, щоб додати функцію.
Проблема полягає в тому, що коли я закінчив, виявилося, що мені потрібно внести незначну зміну інтерфейсу, щоб його вмістити. Тож я змінив. І тоді споживчий клас не може бути реалізований з його поточним інтерфейсом з точки зору нового, тому він також потребує нового інтерфейсу. Тепер минуло три місяці, і мені довелося виправляти незліченні практично непов'язані проблеми, і я дивлюся на вирішення проблем, які були дорожні за рік з цього часу або просто перелічені як не виправлені через труднощі до того, як річ складеться знову.
Як я можу уникнути подібного роду каскадних реконструкцій у майбутньому? Це просто симптом моїх попередніх занять, що занадто сильно залежать один від одного?
Коротка редакція: У цьому випадку рефактор був особливістю, оскільки рефактор збільшив розширюваність певного коду і зменшив деяке з'єднання. Це означало, що зовнішні розробники можуть зробити більше, що було особливістю, яку я хотів отримати. Тож оригінальний рефактор сам по собі не повинен був бути функціональною зміною.
Більша редакція, яку я обіцяв п'ять днів тому:
Перш ніж я розпочав цей рефактор, у мене була система, де я мав інтерфейс, але в реалізації я просто dynamic_cast
через всі можливі реалізації, які я постачав. Це, очевидно, означало, що ви не можете просто успадкувати інтерфейс, з одного боку, по-друге, що ніхто не зможе отримати доступ до реалізації, щоб реалізувати цей інтерфейс. Тож я вирішив, що хочу виправити цю проблему та відкрити інтерфейс для громадського споживання, щоб хто-небудь міг її реалізувати і щоб реалізація інтерфейсу була необхідною для всього договору - очевидно, вдосконалення.
Коли я знаходив і вбив вогнем усі місця, що я це зробив, я знайшов одне місце, яке виявилося особливою проблемою. Це залежало від деталей реалізації всіх різних похідних класів та дублюваної функціональності, яка вже була реалізована, але краще десь в іншому місці. Він міг бути реалізований замість загальнодоступного інтерфейсу і повторно використовувати існуючу реалізацію цієї функціональності. Я виявив, що для коректного функціонування потрібен певний фрагмент контексту. Грубо кажучи, попередня реалізація виклику виглядала якось так
for(auto&& a : as) {
f(a);
}
Однак, щоб отримати цей контекст, мені потрібно було змінити його на щось подібне
std::vector<Context> contexts;
for(auto&& a : as)
contexts.push_back(g(a));
do_thing_now_we_have_contexts();
for(auto&& con : contexts)
f(con);
Це означає, що для всіх операцій, які раніше були частиною f
, деякі з них потрібно зробити частиною нової функції, g
яка працює без контексту, а деякі з них потрібно скласти з частини відкладеної зараз f
. Але не всі методи f
викликають потребу або хочуть цей контекст - деякі з них потребують виразного контексту, який вони отримують окремими засобами. Отже, для всього, що f
закінчується дзвінками (що, грубо кажучи, майже все ), я повинен був визначити, який, якщо такий є, контекст їм потрібен, звідки вони мають його взяти, і як розділити їх на старе f
на нове f
і нове g
.
І ось так я опинився там, де зараз є. Єдина причина, по якій я продовжувала йти, - це те, що мені так чи інакше знадобилося це рефакторинг з інших причин.