Чи корисно міні-рефакторний код, сподіваючись на покращення якості, чи це просто "переміщення коду" без особливої ​​користі?


12

Приклад

Я натрапив на монолітний код, який робить "все" в одному місці - завантажуючи дані з бази даних, показуючи розмітку HTML, виконуючи роль маршрутизатора / контролера / дії. Я почав застосовувати код баз даних SRP, що рухається, до власного файлу, забезпечуючи кращу назву речей, і все це виглядало добре, але потім у мене виникли сумніви, чому я це роблю.

Чому рефактор? Яка мета? Це марно? Яка користь? Зауважте, що я здебільшого залишив монолітний файл таким, яким він є, але відновив лише меншу частину, що стосувалася області, де мені потрібно було виконати певну роботу.

Оригінальний код:

Щоб навести конкретний приклад, я натрапив на цей фрагмент коду - він завантажує специфікації продукту або за відомим ідентифікатором продукту, або за обраним користувачем ідентифікатором версії:

if ($verid)
    $sql1 = "SELECT * FROM product_spec WHERE id = " . clean_input($verid);
else
    $sql1 = "SELECT * FROM product_spec WHERE product_id = " . clean_input($productid) ;
$result1 = query($sql1);
$row1 = fetch_array($result1);
/* html markup follows */

Реконструкція:

Оскільки я виконую деяку роботу, яка вимагає від мене змінити речі в цій конкретній частині коду, я змінив її для використання шаблону репозиторію та оновив його до використання об'єктно-орієнтованих засобів MySQL:

//some implementation details omitted 
$this->repository = new SpecRepository($mysql);
if ($verid)
    $row1 = $this->repository->getSpecByVersion($verid);
else 
    $row1 = $this->repository->getSpecByProductId($productid);
/* html markup follows to be refactored or left alone till another time*/

//added new class:
class SpecRepository extends MySqlRepository
{

    function getSpecByVersion(int $verid)
    {
        return $this->getMySql()->paramQuery("
            SELECT * FROM product_spec WHERE id = ?
        ", $verid)->getSingleArray();
    }

    function getSpecByProductId(int $productid)
    {
        return $this->getMySql()->paramQuery("
            SELECT * FROM product_spec WHERE product_id = ?
        ", $productid)->getSingleArray();
    }
}

Чи варто це робити?

Озираючись на зміни, код все ще є, код з однаковою функціональністю, але в різних файлах, різних назвах, місцях, використовуючи більше об'єктно-орієнтований стиль, а не процедурний. Насправді смішно відзначити, що відреставрований код виглядає набагато більш роздутим, незважаючи на той самий функціонал.

Я передбачаю кілька відповідей, які говорять "якщо ви не знаєте причин, через які ви робите рефактор, не робіть цього", і, можливо, я можу погодитися. Мої причини полягають у покращенні якості коду з часом (і я сподіваюся, що я це зроблю, дотримуючись SRP та інших принципів).

Це досить вагомі причини чи я витрачаю час на "перестановку коду" таким чином? Загалом рефакторинг - це трохи схоже на те, що я бродячи водою, якщо чесно - це потребує часу, і воно стає більш "відокремленим", наскільки йде СРП, але, незважаючи на мої добрі наміри, я не відчуваю, що я встигаю зробити дивовижні поліпшення. Отже, дискусія про те, чи найкраще залишати код як раніше, а не рефактор.

Чому я в першу чергу рефактор?

У моєму випадку я додаю нову функціональність для нової лінійки продуктів, тому мені доведеться або слідувати існуючій структурі коду для подібних ліній продуктів, або писати власну.


OT, але слід враховувати те, що Select * from ..можна вважати анти-закономірністю. Дивіться stackoverflow.com/q/3639861/31326
Пітер М

1
@PeterM: Якщо ви не пишете ORM, в цьому випадку це select *стає "найкращою практикою".
Роберт Харві

@RobertHarvey У такому випадку я запитую, чому ви пишете власну ORM: D
Пітер М

Я зробив рефактори для більш невідповідних причин. Скорочення технічної заборгованості добре, як тільки користь перевищує витрати. Схоже, це ваш випадок. Питання: Чи є у вас правильні тести, щоб переконатися, що код все ще працює, як очікувалося?
Лаїв

1
Надійність повинна бути переважаючою метрикою при рефакторингу ІМО, а не ремонтопридатність, яка стосується змінності, оскільки надійність знижує в першу чергу причини зміни. Це покращує стабільність, і код, який є абсолютно стабільним і надійним і виконує все необхідне, несе невеликі витрати на обслуговування, оскільки у нього є дуже мало причин коли-небудь потрібно змінювати. Прагніть зменшити причини зміни речі, а не намагатися зробити їх якомога простішими. Шукаючи перших, також схильні дати деякі з останніх.

Відповіді:


13

На конкретному прикладі, який ви навели, рефакторинг, можливо, не знадобився б, принаймні, поки що. Але ви побачили, що існує потенціал для Messier-коду в майбутньому, що призвело б до більш тривалого та виснажливого рефакторингу, тому ви взяли на себе ініціативу та прибирали речі зараз.

Я б також сказав, що перевагу, яку ви отримали тут, - це код, який легше зрозуміти, принаймні, з моєї точки зору, як той, хто не знає вашої кодової бази.


Загалом:

Чи спрощує рефакторинг іншим розробникам зрозуміти ваш код?

Чи спрощує покращення коду рефакторинг?

Чи спрощує оновлення коду рефакторинг?

Чи робить рефакторинг простішим налагодження?

Якщо відповідь на будь-яке з них було «так», то воно того варте, і це було більше, ніж просто «переміщення коду?

Якщо відповідь на все це було «ні», то, можливо, це не потрібно було переробляти.

Кінцевий розмір бази коду може бути більшим з точки зору LoC (хоча деякі рефактори можуть зменшити базу коду шляхом упорядкування зайвого коду), але це не повинно бути фактором, якщо є інші вигоди.


9

Рефакторинг розквітне, якщо ви розбираєте моноліт.

Однак ви вже знайшли користь.

"У моєму випадку я додаю нову функціональність для нової лінійки продуктів."

Ви не можете додати функціональність до моноліту дуже легко, не руйнуючи речі.

Ви сказали, що той самий код отримує дані та робить розмітку. Чудово, поки ви не хочете змінити, які стовпці ви вибираєте та маніпулюєте для показу у певних умовах. Раптом ваш розмітний код у певному випадку перестає працювати, і вам доведеться робити рефактор.


так, але я також можу продовжувати додавати до моноліту навіть для нового функціоналу :), тобто я додав би новий if/elseблок і дотримувався оригінального стилю коду, щоб додати SQL-висловлювання, і тоді я поставив би нову розмітку HTML у той самий монолітний файл. І щоб змінити стовпці, я би знайшов блок if / else, відповідальний за розміщення рядків SQL, і відредагував би цей SQL, щоб внести зміни до стовпців, не порушуючи цей файл.
Денніс

З рефакторованим підходом я, швидше за все, спершу перейду до блоку if / else в моноліті, щоб побачити, що мені потрібно перейти до окремого файлу репозиторію, щоб змінити SQL. Або якби я нещодавно працював над цим монолітом, я б знав, щоб замість нього перейти у файл сховища, минаючи моноліт. Або якщо я шукав мою кодову базу для конкретного SQL, пошук міг би ввести мене в новий файл репозиторію, також обходячи моноліт
Денніс

2

Я розглядаю рефакторинг, якщо знаю, що мені доведеться працювати з тим проектом місяці чи навіть роки. Якщо мені доведеться змінити тут і там, ніж я протистояти собі потягу пересувати код.

Кращим способом робити рефакторинг є те, коли у мене є базовий код з якимись автоматизованими тестами. Тим не менш, я повинен бути дуже обережним з тим, що я змінюю, щоб переконатися, що все працює, як раніше. Ви дуже швидко втратите довіру, якщо будете доставляти помилки в інших областях, ніж у тій, у якій ви повинні працювати.

Я ціную рефакторинг з наступних причин:

  • Я краще розумію код
  • Я видаляю дубльований код, тому якщо у мене в одному місці помилка, я не повинен шукати всюди, де був вставлений код
  • Я створюю класи, які мають чітко визначену роль. Наприклад, я можу повторно використовувати той самий сховище для гідратації HTML-сторінки, каналу XML або фонового завдання. Це було б неможливо, якщо код доступу до бази даних живе лише у класі HTML
  • Я перейменую змінні, методи, класи, модулі, папки, якщо вони не відповідають поточній дійсності; Я commentкоду через імена змінних, назви методів
  • Я вирішую складні помилки з комбінацією між тестовими одиницями та рефакторингом
  • У мене є можливість замінити цілі модулі, пакети. Якщо поточна ORM застаріла, невдала або не підтримується, її легше замінити, якщо вона не поширюється по всьому проекту.
  • Я відкриваю нові можливості або вдосконалення, які призводять до пропозицій для замовника.
  • Я створив багаторазові компоненти. Це було однією з найбільших переваг, оскільки робота, виконана в цьому проекті, могла бути повторно використана для іншого клієнта / проекту.
  • Я часто починаю з найбільш глупого, жорсткого, нерозумного рішення, і перетворюю його пізніше, як тільки я дізнаюся більше. Цей пізніший момент може бути сьогодні чи, можливо, через кілька днів. Я не хочу витрачати занадто багато часу на architectрішення. Це станеться під час написання - переписування коду вперед і назад.

У досконалому світі рефактори слід перевіряти одиничними тестами, інтеграційними тестами тощо. Якщо таких немає, спробуйте додати їх. Також деякі IDE можуть дуже допомогти. Зазвичай я використовую плагін, який коштує грошей. Я хочу провести мало часу з рефакторингами і бути дуже ефективною щодо цього.

Я також представив помилок. Я можу зрозуміти, чому деякі питання QA запитують are you guys doing refactoring? because everything stopped working!Це ризик, який має сприйняти команда, і ми завжди повинні бути прозорими щодо цього.

Протягом багатьох років я виявив, що постійне рефакторинг покращує мою продуктивність. Набагато простіше було додати нові функції. Також великі перебудови не потребували цілого перезапису, як це часто траплялося, коли база коду не була адаптована до еволюції продукту. Також це весело.


Це дуже близьке до голосування "за" - "Я" у "Комп'ютерному програмісті" немає. Під «я» ти мені «я» (голосування вниз) або ти маєш на увазі розробників, які працюють над кодом зараз і в майбутньому?
mattnz

Я це також помітив, але я виклав це на формулюванні. Також я багато років працював соло-розробником, і коли я написав цю відповідь, я повторив здебільшого свій досвід. Я міг би замінити Iз toхоча.
Адріан Іфтоде

1

Я думаю, що питання, яке ви повинні задати собі, не так вже й багато: "Чи є користь?" але "Хто заплатить за цю роботу?"

Всі ми можемо сперечатися про сприйняті переваги до кінця днів, але якщо у вас є клієнт, який вірить у ці вигоди та цінує їх достатньо, щоб заплатити за зміни, ви не повинні їх робити.

Це занадто просто, коли ти десь знаходишся в команді розробників, щоб побачити код і хочеш переробити його, щоб зробити його "кращим". Але ставити значення на «кращий код», коли продукт вже працює, дуже важко.

Такі речі, як "швидший розвиток нових функцій", не вирішують це, оскільки вони просто скорочують витрати. Вам потрібно генерувати продажі.


1

На мій погляд, рефакторинг - це хороша інвестиція.

Інакше незабаром ви отримаєте технічну заборгованість (невирішені проблеми, поганий код, який працює лише за певних умов тощо). Невдовзі технічна заборгованість збільшуватиметься та збільшуватиметься, доки програмне забезпечення не стане ремонтом.

Але для того, щоб зробити рефакторинг, ви також повинні інвестувати в тести. Вам слід створити автоматизовані тести, в ідеалі ви повинні мати одиничні тести та інтеграційні тести. Це першенець вас від порушення існуючого коду.

Якщо вам доведеться переконати свого начальника чи колег, вам слід прочитати кілька книг про спритні методи (наприклад, SCRUM) та тестові розробки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.