Як запобігти невідомому дублюванню коду?


33

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

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

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

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

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

У мене був один експеримент із додаванням функцій корисності як методів розширення (мені не потрібні були внутрішні класи класу, і це, безумовно, потрібно було лише у дуже конкретних сценаріях). Це призвело до запобігання захаращуванню первинного класу і подібного, але це насправді вже не можна виявити, якщо ви вже не знаєте про це


Відповіді:


30

Проста відповідь полягає в тому, що ви дійсно не можете запобігти дублюванню коду. Однак ви можете "виправити це" через складний безперервний повторюваний поступовий процес, який зводиться в два етапи:

Крок 1. Почніть писати тести на застарілий код (бажано, використовуючи тестову рамку)

Крок 2. Перепишіть / рефакторний код, який дублюється, використовуючи те, що ви дізналися з тестів

Ви можете використовувати інструменти статичного аналізу для виявлення дублювання коду, а для C # є безліч інструментів, які можуть це зробити для вас:

Такі інструменти допоможуть вам знайти точки в коді, що робить подібні речі. Продовжуйте писати тести, щоб визначити, що вони дійсно роблять; використовуйте ті ж тести, щоб зробити дублікат коду більш простим у використанні. Цей "рефакторинг" можна виконати декількома способами, і ви можете використовувати цей список, щоб визначити правильний:

Крім того, існує ціла книга на цю тему Майкла К. Пір'я, Ефективно працюючи зі Спадковим кодексом . Поглиблено розглядаються різні стратегії, які можна вжити, щоб змінити код на краще У нього є "алгоритм зміни коду", який не за горами від двох етапів вище:

  1. Визначте точки зміни
  2. Знайдіть тестові бали
  3. Розбийте залежності
  4. Пишіть тести
  5. Внесіть зміни та рефактор

Книгу добре читати, якщо ви маєте справу з розробкою коричневого поля, тобто спадковим кодом, який потрібно змінити.

В цьому випадку

У випадку з ОП, я можу уявити, що непереборний код викликається медовим горщиком для "корисних методів та хитрощів", ​​які мають декілька форм:

  • статичні методи
  • використання статичних ресурсів
  • однокласні заняття
  • магічні значення

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

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

Оскільки ОП є новим для коду, перед тим, як щось робити, слід зробити ще деякі речі:

  • Знайдіть час, щоб навчитися з кодової бази, тобто зламати "все", протестувати "все", повернути назад.
  • Попросіть когось із команди переглянути ваш код, перш ніж здійснити його. ;-)

Удачі!


Насправді у нас є дуже багато тестування одиниць та інтеграції. Не 100% охоплення, але деякі речі, які ми робимо, майже неможливо перевірити без тест-змін без нашої кодової бази. Я ніколи не думав використовувати статичний аналіз для пошуку дублювання. Мені доведеться спробувати це далі.
Граф

@Earlz: Статичний аналіз коду є приголомшливим! ;-) Крім того, щоразу, коли вам потрібно буде внести зміни, придумайте рішення, щоб полегшити зміни (перевірте це для рефактора каталогів шаблонів)
Spoike

+1 Я розумію, якщо хтось покладе нагороду на цей Q, щоб нагородити цю прихильницю як "додаткову корисну". Каталог Refactor to Patterns - це золото, такі речі, як це в моді GuidanceExplorer.codeplex.com - чудові посібники для програмування.
Джеремі Томпсон

2

Ми також могли б спробувати побачити проблему з іншого кута. Замість того, щоб думати, що проблема полягає в дублюванні коду, ми можемо врахувати, чи проблема виникає у відсутності політики для повторного використання коду.

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

Автор цієї книги Йоганнес Самітінгер описує набір перешкод для повторного використання коду, деякі концептуальні, технічні. Наприклад:

Концептуально-технічна

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

До інших основних технічних труднощів відносяться

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

За словами автора, різні рівні повторного використання трапляються залежно від зрілості організації.

  • Тимчасове повторне використання серед прикладних груп : якщо немає явного зобов’язання повторного використання, то повторне використання може відбуватися в кращому випадку неофіційним та випадковим чином. Більша частина повторного використання, якщо така є, відбудеться в межах проектів. Це також призводить до очищення коду і закінчується дублюванням коду.
  • Повторне використання на основі репозиторіїв між групами додатків : ситуація дещо покращується, коли використовується сховище компонентів і до нього можуть отримати доступ різні групи додатків. Однак явного механізму введення компонентів у сховище не існує, і ніхто не несе відповідальності за якість компонентів у сховищі. Це може призвести до багатьох проблем і перешкодити повторному використанню програмного забезпечення.
  • Централізоване повторне використання з групою компонентів: У цьому сценарії група компонентів явно відповідає за сховище. Група визначає, які компоненти слід зберігати у сховищі, та забезпечує якість цих компонентів та наявність необхідної документації, а також допомагає отримати відповідні компоненти у конкретному сценарії повторного використання. Групи додатків відокремлені від групи компонентів, яка виступає своєрідним субпідрядником для кожної групи заявок. Завдання групи компонентів - мінімізувати надмірність. У деяких моделях члени цієї групи також можуть працювати над конкретними проектами. Під час запуску проекту їх знання є цінними для сприяння повторному використанню, і завдяки їх участі у конкретному проекті вони можуть визначити можливих кандидатів для включення до сховища.
  • Повторне використання на основі домену : спеціалізація груп компонентів дорівнює повторному використанню на основі домену. Кожна група доменів відповідає за компоненти в своєму домені, наприклад, мережеві компоненти, компоненти інтерфейсу користувача, компоненти бази даних.

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


1

Є 2 можливих рішення:

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

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

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


0

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

Якщо мова є C / C ++, то дублювання злиття буде простішим через гнучкість зв’язку (можна зателефонувати до будь-яких externфункцій без попередньої інформації). Для Java або .NET може знадобитися розробити допоміжні класи та / або утилітні компоненти.

Зазвичай я починаю видалення дублювання існуючого коду лише в тому випадку, якщо основні помилки виникають із дублюваних частин.


0

Це типова проблема більш масштабного проекту, яким займаються багато програмістів, які вносять свій внесок у інколи великий тиск однолітків. Дуже спокусливо зробити копію класу та адаптувати її до цього конкретного класу. Однак, коли проблема була знайдена в початковому класі, її слід вирішити і в її похибки, про які часто забувають.

Для цього є рішення, і воно називається Generics, яке було представлено на Java 6. Це еквівалент C ++, який називається Template. Код, точний клас якого ще не відомий у загальному класі. Перевірте наявність Java Generics, і ви знайдете тонни і тонни документації на неї.

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

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

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

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

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

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

І так, я дуже підтримую автоматичне тестування коду. На початку це коштуватиме дорожче, але це, безумовно, заощадить вам величезну кількість часу в цілому. І спробуйте домогтися кодового покриття загальним принаймні 80% та 100% для загального коду.

Сподіваюся, що це допоможе і удачі.


0

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

Якщо, скажімо, ви даєте мені можливість використання:

A) Стабільна (незмінна) і крихітна бібліотека зображень, добре перевірена , яка дублює кілька десятків рядків тривіального математичного коду для векторної математики, як крапкові вироби та лерпи та затискачі, але повністю відокремлена від усього іншого і складається на частку секунда.

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

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

Але також є можливість зв'язати і стабільність, і дещо скромне дублювання тут і там може служити механізмом роз'єднання, що також збільшує стабільність (незмінний характер) пакета.

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


-2

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

Причин цього може бути три:

  1. Деяка тема навколо цього завдання однакова для обох модулів. У цьому випадку дублювання коду є поганим і його слід ліквідувати. Було б розумно створити клас або модуль для підтримки цієї теми та використовувати її методи в обох модулях.

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

  3. Але в інших випадках збіг завдань - тимчасовий збіг і не більше того. Було б небезпечно вірити, що ці завдання залишаться незмінними під час змін проекту через рефакторинг та навіть налагодження. У цьому випадку було б краще створити дві однакові функції / фрагменти коду в різних місцях. І майбутні зміни в одній з них не торкнуться іншої.

І цей 3-й випадок трапляється дуже часто. Якщо ти дублюєш "несвідомо", в основному саме з цієї причини - це не справжнє дублювання!

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


2
code duplication is not always harmful- одна погана порада.
Тулен Кордова

1
Чи повинен я поклонитися вашому авторитету? Я тут виклав свої причини. Якщо я помиляюся, покажіть, де помилка. Зараз це здається вашою слабкою здатністю вести дискусію.
Gangnus

3
Копіювання коду є однією з основних проблем у розробці програмного забезпечення, і багато вчених-теоретиків з обчислювальної техніки розробили парадигми та методології лише для того, щоб уникнути дублювання коду як основного джерела проблем ремонту в розробці програмного забезпечення. Це як сказати, що "писати поганий код не завжди є поганим", таким чином все може бути риторично виправданим. Можливо, ви праві, але уникати дублювання коду - це занадто хороший принцип, щоб жити так, щоб заохочувати протилежне ..
Tulains Córdova

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

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