Программирование методом копирования-вставки
Программирование методом копирования-вставки — процесс создания программного кода с часто повторяющимися частями, произведёнными операциями копировать-вставить[1][2]. Обычно этот термин используется в уничижительном значении для обозначения недостаточных навыков программирования либо отсутствия выразительной среды разработки, где обычно возможно использовать подключаемые библиотеки.
Программирование методом копирования-вставки — распространённый антипаттерн, приводящий к появлению дублированного кода, зачастую большого объёма и сложного для чтения. Повторяемые фрагменты кода размножают ошибку, допущенную в оригинальном коде, а многочисленные повторы усложняют исправление этой ошибки во всех копиях[1][3].
Существуют ситуации, когда копипаста в программировании может быть оправдана или необходима: шаблоны, размотка цикла, а также при использовании некоторых парадигм программирования или работе в редакторе исходного кода с сниппетами.
Плагиат
Копирование-вставка часто используется неопытными или начинающими программистами, которые затрудняются писать код с нуля и предпочитают искать ранее реализованные решения или их части и использовать их как основу для своей задачи.
Программисты, часто копирующие чужой код, могут не понимать его полностью или частично. Проблема возникает скорее из-за их неопытности и недостатка настойчивости, чем из-за самого факта копирования. Код берётся у знакомых, коллег, с интернет-форумов, от преподавателей или из книг по программированию. Результат может быть несвязным набором стилей и содержать ненужный код для уже неактуальных проблем.
Существует различие между программированием методом копировать-вставить и программированием типа карго-культ. В первом случае речь идёт о множественном дублировании фрагментов программы, тогда как во втором — как копирование решения задачи без осмысления принципов работы кода, так и копирование частей кода без необходимости[4].
Дополнительная проблема — возможное копирование уже содержащихся в коде ошибок. Приёмы проектирования, использованные в разных исходных кодах, могут быть неприемлемы при объединении в новой среде.
Такой код случайно может стать обфусцированным, так как имена переменных, классов, функций и т. п. после копирования, как правило, не меняются, даже если их смысл в новом контексте другой.
Дублирование
Будучи разновидностью дублирования кода, C&P-программирование обладает рядом проблем, особенно если копия теряет связь с оригиналом. Тогда при необходимости изменений усилия тратятся на поиск всех дубликатов. Хорошие комментарии могут частично ускорить этот процесс, но не отменяют необходимости множества правок. Комментарии, описывающие, где искать дубликаты, быстро устаревают.
Эрик Аллен в книге «Типичные ошибки проектирования» называет ошибку программирования методом копирования термином «Фальшивая черепица». Вынесение дубликата в отдельный метод (основной «рецепт» решения проблемы) может оказаться непростой задачей[5].
Использование библиотек
Программирование методом копирования-вставки встречается и среди опытных разработчиков, имеющих библиотеки хорошо протестированных и готовых к применению сниппетов и общих алгоритмов, настраиваемых для конкретных задач[2].
Вместо создания множества клонированных вариантов обобщённого алгоритма объектно-ориентированное программирование предлагает абстрагировать алгоритм в инкапсулированный класс для повторного использования. Такой класс предоставляет возможность гибкого расширения посредством наследования и перегрузки, экономя усилия программиста, поскольку работать приходится только с одним исходным кодом[6]. С расширением функциональности увеличиваются и библиотеки, сохраняя обратную совместимость. При исправлении бага во внедрённом алгоритме все продукты, использующие эту библиотеку, автоматически получают исправление.
Ветвление
Ветвление — стандартная практика работы в больших командах разработчиков, позволяющая вести параллельную разработку и сократить цикл вывода на рынок. Классическое ветвление имеет следующие черты:
- Управляется системой контроля версий, поддерживающей ветвление;
- Ветви объединяются по завершении работ.
Программирование методом копировать-вставить — менее формализованная альтернатива классическому ветвлению, часто применяемая, если предполагается существенное расхождение кода в ветвях со временем, например, при выделении нового программного продукта из существующего.
Как способ выделения нового продукта, копипаста даёт следующие преимущества:
- Нет необходимости в регрессионном тестировании оригинального продукта;
- Экономия времени на обеспечение качества;
- Сокращение времени вывода на рынок;
- Нет риска внедрения новых ошибок в уже существующий продукт (важно для сохранения клиентской базы).
Недостатки:
- Если новый и исходный продукт различаются меньше, чем ожидалось, может возникнуть необходимость поддерживать две базы кода (что удваивает издержки) для по сути схожих продуктов. Это приводит к большому объёму рефакторинга и ручному объединению в будущем;
- Две кодовые базы увеличивают время внесения общих изменений, что способно нивелировать выигранное время. В реальности такой подход часто приводит к убыткам.
Ещё одна альтернатива — модульный подход:
- Общий для нескольких продуктов код изначально выносится в библиотеки/модули;
- Использование библиотеки становится основой для нового продукта;
- Для множества производных продуктов такой подход даёт эффект увеличения темпа разработки после второго продукта[7].
Повторяющиеся задачи или вариации задачи
Особенно вредна C&P-практика, ведущая к появлению дублированного кода, выполняющего одну и ту же задачу с вариациями. Каждый экземпляр — это копия предыдущего с небольшими отличиями. В результате:
- Копипаста приводит к разрастанию методов;
- Каждый экземпляр — это отдельный дубликат с уже описанными выше проблемами, но зачастую в большем масштабе (десятки, а иногда сотни дубликатов). Исправление ошибок усложняется[8];
- В подобном коде тяжело ориентироваться, различия между копиями сложно выявить, что усложняет внесение изменений;
- Процедурное программирование настоятельно не рекомендует используемый здесь подход. Правильнее выносить повторяющуюся логику в функцию или подпрограмму, вызываемую в цикле. Такой подход называют «хорошо декомпозированным кодом», он проще для понимания и расширения[9];
- Основное эмпирическое правило: «не повторяйся». Дэвид Парнас сформулировал его так: «Копирование и вставка кода — результат ошибки проектирования»[10].
Умышленный выбор подхода
Иногда копипаста в программировании используется сознательно, например, для шаблонов: объявления класса, подключения стандартных библиотек, или при копировании шаблона (с пустыми блоками или функциями-заглушками) в качестве основы для заполнения.
Использование идиом программирования и паттернов схоже с техникой копировать-вставить, поскольку основывается на шаблонном коде. В одних случаях это — фрагменты, вставляемые по мере надобности, в других — знания, вырабатываемые опытом. Во многих случаях такие идиомы выносятся в отдельные функции/методы, если код велик, или просто встраиваются вручную, если фрагмент короток.
Пример
Пример корректного использования копипасты — шаблон цикла 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) {
}
Многие программисты дублируют похожие строки (например, вызов функции для двух объектов с похожими именами), потому что быстрее продублировать строку, чем написать новую. Однако вероятность ошибки — особенно в последней строке — не уменьшается[11], а часто даже возрастает[12].
При необходимости нескольких правок в строке-копии ошибки встречаются чаще. Например, ниже после дублирования строки изменено присваиваемое значение, но не исправлен индекс массива:
mArray[12] = "a";
mArray[13] = "b";
mArray[14] = "c";
mArray[14] = "d";
Существуют исследования, оправдывающие копирование-вставку как модель взаимодействия — например, язык программирования Subtext[13]: в этой модели копипаста — основной способ работы и потому не считается антипаттерном.
Примечания
- ↑ 1 2 Miryung Kim, Lawrence Bergman, Tessa Lau, David Notkin. Ethnographic Study of Copy and Paste Programming Practices in OOPL (англ.) (PDF) (2004). doi:10.1109/ISESE.2004.1334896. Дата обращения: 3 ноября 2013. Архивировано 22 декабря 2015 года.
- ↑ 1 2 Patricia Jablonski, Daqing Hou. CReN: A Tool for Tracking Copy-and-Paste Code Clones and Renaming Identifiers Consistently in the IDE (англ.) (PDF). Нью-Йорк, США: ACM New York (2005). doi:10.1145/1328279.1328283. Дата обращения: 3 ноября 2013. Архивировано 22 декабря 2015 года.
- ↑ Chanchal Kumar Roy, James R. Cordy. A Survey on Software Clone Detection Research (англ.). Онтарио, Канада: Queen’s University, Kingston (26 сентября 2007). Дата обращения: 3 ноября 2013. Архивировано 22 декабря 2015 года.
- ↑ Gordon Fletcher. Cargo Cults in Java (англ.) С. 3. Великобритания: University of Salford (2004). Дата обращения: 4 ноября 2011. Архивировано 22 декабря 2015 года.
- ↑ Аллен, Эрик. Типичные ошибки проектирования. Библиотека программиста. — СПб. : Питер, 2003. — P. 73—82. — ISBN 5-88782-304-6.
- ↑ Raymond Wallen. 4 major principles of Object-Oriented Programming (англ.). codebetter.com (19 июля 2005). Дата обращения: 4 ноября 2013. Архивировано 12 декабря 2013 года.
- ↑ Lisa Wold Eriksen. Code Reuse In Object Oriented Software Development (англ.). Norwegian University of Science and Technology, Department of Computer and Information Science (2004). Дата обращения: 4 ноября 2011. Архивировано 1 апреля 2011 года.
- ↑ The Benefits of Coding Standards, Richard Sharpe. The Benefits of Coding Standards (англ.). JAX Magazine. Дата обращения: 6 января 2017. Архивировано 6 октября 2008 года.
- ↑ Stanford University, CS 106X ("Programming Abstractions") Course Handout: "Decomposition". Stanford University (18 января 2008). Дата обращения: 6 января 2017. Архивировано 16 мая 2008 года.
- ↑ Макконнелл, Стив. Совершенный код. Мастер-класс. — СПб. : Питер, 2005. — P. 553. — ISBN 5-7502-0064-7.
- ↑ Андрей Карпов. Последствия использования технологии Copy-Paste при программировании на Си++ и как с этим быть. PVS-Studio, Статический анализатор кода для C/C++/C++11 (24 января 2011). Дата обращения: 3 ноября 2013. Архивировано 28 февраля 2021 года.
- ↑ Андрей Карпов. Эффект последней строки. PVS-Studio, Статический анализатор кода для C/C++/C++11 (31 мая 2014). Дата обращения: 13 сентября 2014. Архивировано 8 июня 2014 года.
- ↑ Jonathan Edwards. Subtext: Uncovering the Simplicity of Programming (англ.) (PDF). MIT CSAIL (2005). Дата обращения: 3 ноября 2013. Архивировано 16 апреля 2015 года.
Литература
- Miryung Kim, Lawrence Bergman, Tessa Lau, David Notkin. Ethnographic Study of Copy and Paste Programming Practices in OOPL (англ.) (PDF) (2004). doi:10.1109/ISESE.2004.1334896. Дата обращения: 3 ноября 2013.
- Patricia Jablonski, Daqing Hou. CReN: A Tool for Tracking Copy-and-Paste Code Clones and Renaming Identifiers Consistently in the IDE (англ.) (PDF). Нью-Йорк, США: ACM New York (2005). doi:10.1145/1328279.1328283. Дата обращения: 3 ноября 2013.
- Chanchal Kumar Roy, James R. Cordy. A Survey on Software Clone Detection Research (англ.). Онтарио, Канада: Queen’s University, Kingston (26 сентября 2007). Дата обращения: 3 ноября 2013.
- Gavriel Yarmish, Danny Kopec. Revisiting Novice Programmers Errors (англ.) (PDF). Нью-Йорк, США: ACM New York (2007). doi:10.1145/1272848.1272896. Дата обращения: 4 ноября 2013.
- Gordon Fletcher. Cargo Cults in Java (англ.) С. 3. Великобритания: University of Salford (2004). Дата обращения: 4 ноября 2011.
- Андрей Карпов. Последствия использования технологии Copy-Paste при программировании на Си++ и как с этим быть. PVS-Studio, Статический анализатор кода для C/C++/C++11 (24 января 2011). Дата обращения: 3 ноября 2013.
- Андрей Карпов. Эффект последней строки. PVS-Studio, Статический анализатор кода для C/C++/C++11 (31 мая 2014). Дата обращения: 13 сентября 2014.
- Макконнелл, Стив. Глава 24. Рефакторинг // Совершенный код. Мастер-класс / Под ред. В. Г. Вшивцева. — 2-е изд. — СПб. : Питер, 2005. — P. 553. — ISBN 5-7502-0064-7.
- Аллен, Эрик. Глава 7. Фальшивая черепица // Типичные ошибки проектирования. Библиотека программиста. — 1-е изд. — СПб. : Питер, 2003. — P. 73—82. — ISBN 5-88782-304-6.