Програмування методом копіювання-вставлення

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

Програмування методом копіювання-вставлення, C&P-програмування або копіпаста в програмуванні — процес створення програмного коду з часто повторюваними частинами, створений операціями копіювати-вставити[1][2]. Зазвичай цей термін використовується в зневажливому розумінні для позначення недостатніх навичок комп'ютерного програмування або відсутності виразного середовища розробки, в якому, як правило, можна використовувати спільні бібліотеки.

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

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

Плагіат[ред. | ред. код]

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

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

Існує певна різниця між програмуванням методом копіювати-вставити і програмуванням типу карго-культ. Під першим мається на увазі сам факт багаторазового дублювання частин коду програми[5], другий тип може мати на увазі як копіювання коду для вирішення завдання, здійснюване з програми або зовнішніх джерел і без розуміння схеми роботи коду, так і копіювання частин коду без необхідності[5][6].

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

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

Дублювання[ред. | ред. код]

Як формі дублювання коду, C&P-програмуванню властиві деякі проблеми, що загострюються, якщо код не зберігає ніякого семантичного зв'язку між оригіналом і копією. У цьому випадку, якщо потрібні зміни, то час витрачається даремно на пошуки всіх частин, що дублюються. Цей процес може бути частково прискорений при добре коментованому коді, але все ж не скасовує необхідність здійснення кількох правок. Оскільки супровід коду часто опускає оновлення коментарів[7], коментарі, які описують, де знайти повторювані частини коду, свідомо застарівають.

Ерік Аллен в книзі «Типові помилки проєктування» використовує термін «Фальшива черепиця» для позначення помилок, що викликаються копіюванням фрагмента програми. Виокремлення повторюваного фрагмента в метод (основний «рецепт» позбавлення від такого роду проблем) може виявитися нетривіальним завданням[8].

Використання бібліотек[ред. | ред. код]

Програмування методом копіювання-вставлення також нерідко застосовується досвідченими програмістами, що мають свої бібліотеки добре протестованих і готових до використання сніпетів і загальних алгоритмів, що пристосовуються до конкретних завдань[2].

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

Розгалуження[ред. | ред. код]

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

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

Як спосіб відокремлення нового продукту, копіпаста має деякі переваги. Оскільки розробка нового продукту не вносить зміни у створений раніше, то:

  • Немає необхідності регресивного тестування наявного продукту;
  • Економиться час, пов'язаний із забезпеченням якості;
  • Скорочується час виходу на ринок;
  • Відсутній ризик внесення нових помилок в наявний продукт (що могло б порушити наявну базу користувачів).

Недоліки:

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

Ще однією альтернативою C&P-підходу є модульний підхід:

  • Спочатку в бібліотеки або модулі виноситься код, який буде спільним для обох продуктів;
  • Використання створених бібліотек є основою для розробки нового продукту;
  • Якщо передбачається існування третьої, четвертої, п'ятої і т. д. похідної версії продукту, то такий підхід набагато сильніше копіювання-вставлення, оскільки різко скорочує цикл розробки будь-якого додаткового продукту після другого[10].

Повторювані завдання або варіації завдання[ред. | ред. код]

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

  • Кожен екземпляр створює дублікат коду з усіма проблемами, описаними раніше, але в набагато більшому масштабі. Зазвичай існують десятки дублікатів, але можливі й сотні. Виправлення помилки стає дуже складним та дорогим завданням[11];
  • У такого роду коді присутні питання зручності та прочитності. Проблеми, пов'язані з труднощами визначення відмінностей між повтореннями мають прямий вплив на ризики і витрати по внесенню правок в код;
  • Модель процедурного програмування наполегливо не рекомендує застосовувати у вирішенні завдань, що повторюються, підхід програмування методом копіювати-вставити. Найкращим рішенням є створення функції або підпрограми, яка виконує один прохід через задачу. Така підпрограма потім викликається батьківською програмою повторно, або в деякій формі циклу. Такий код називається «добре декомпонованим» і рекомендується як легко читаємий і більш готовий до розширення[12];
  • Основна емпірична закономірність для такого випадку: «Не повторюйся». Девід Парнас сформулював це правило так: «Копіювання і вставлення коду — наслідок помилки проєктування»[13].

Навмисний вибір підходу[ред. | ред. код]

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

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

Приклад[ред. | ред. код]

Простим прикладом допустимого застосування підходу може бути цикл for, який може виглядати як for (int i=0; i!=n; ++i) {}. Прикладом коду, що використовує такий цикл може бути:

void foo(int n) {
    for (int i=0; i!=n; ++i) {
    }
}

Код циклу може бути згенерований наступним сніпетом (визначаючи типи і імена змінних):

for ($type $loop_var = 0; $loop_var != $stop; ++$loop_var) {
}

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

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

mArray[12] = "a";
mArray[13] = "b";
mArray[14] = "c";
mArray[14] = "d";

Існує дослідження[16], спрямоване на «декриміналізацію» програмування методом копіювати-вставити — Subtext (мова програмування)[en]. Слід зазначити, що в цій моделі, копіпаста — основна модель взаємодії і, отже, не розглядається як антипатерн.

Див. також[ред. | ред. код]

Джерела[ред. | ред. код]

  1. а б Miryung Kim; Lawrence Bergman, Tessa Lau, David Notkin (2004). Ethnographic Study of Copy and Paste Programming Practices in OOPL (PDF) (англ.). doi:10.1109/ISESE.2004.1334896. Процитовано 3 листопада 2013.
  2. а б Patricia Jablonski, Daqing Hou (2005). CReN: A Tool for Tracking Copy-and-Paste Code Clones and Renaming Identifiers Consistently in the IDE (PDF) (англ.). Нью-Йорк, США: ACM New York. doi:10.1145/1328279.1328283. Процитовано 3 листопада 2013.
  3. Chanchal Kumar Roy, James R. Cordy (26 вересня 2007). A Survey on Software Clone Detection Research (англ.). Ontario, Canada: Queen's University, Kingston. Процитовано 3 листопада 2013.
  4. а б Gavriel Yarmish. Danny Kopec (2007). Revisiting Novice Programmers Errors (PDF) (англ.). Нью-Йорк, США: ACM New York. doi:10.1145/1272848.1272896. Процитовано 4 листопада 2013.
  5. а б Jason Rogers, Chuck Pheatt (May 2009). Integrating Antipatterns into the Computer Science Curriculum. Journal of Computing Sciences in Colleges (англ.). Consortium for Computing Sciences in Colleges. с. 187. Процитовано 4 листопада 2013.
  6. Gordon Fletcher (2004). Cargo Cults in Java (англ.). University of Salford. с. 3. Процитовано 4 листопада 2011.
  7. Robert Pittenger (6 травня 2008). Building ASP.NET Web Pages Dynamically in the Code-Behind (англ.). codeproject.com. Процитовано 4 листопада 2013.
  8. Типові помилки проєктування, 2003.
  9. Principles of Object-Oriented Programming.
  10. Code Reuse In OO Development.
  11. The Benefits of Coding Standards.
  12. CS 106X.
  13. Досконалий код, 2005.
  14. Карпов, 2011.
  15. Карпов, 2014.
  16. Sub Text.

Література[ред. | ред. код]