В компьютерном программировании дублирующийся код — это последовательность исходного кода , которая встречается более одного раза либо внутри программы, либо в разных программах, принадлежащих или поддерживаемых одной и той же организацией. Дублирующийся код обычно считается нежелательным по ряду причин. [1] Обычно к количеству кода, который должен присутствовать в последовательности, применяется минимальное требование, чтобы его считали дублирующим, а не случайно похожим. Последовательности дублированного кода иногда называют клонами кода или просто клонами. Автоматизированный процесс поиска дубликатов в исходном коде называется обнаружением клонов.
Две кодовые последовательности могут быть дубликатами друг друга, но при этом не быть идентичными посимвольно, например, будучи посимвольно идентичными только тогда, когда пробельные символы и комментарии игнорируются, или будучи идентичными посимвольно или токеном. -для токена идентично со случайными вариациями. Даже последовательности кода, идентичные только функционально, могут считаться дублирующим кодом.
Некоторые из способов создания дублированного кода:
Также может случиться так, что требуется функциональность, очень похожая на ту, что есть в другой части программы, и разработчик самостоятельно пишет код, очень похожий на тот, что существует где-то еще. Исследования показывают, что такой независимо переписанный код обычно не является синтаксически схожим. [2]
Еще одной причиной дублирования является автоматически генерируемый код, где дублирование кода может быть желательно для увеличения скорости или простоты разработки. Обратите внимание, что фактический генератор не будет содержать дубликатов в своем исходном коде, а только выходные данные, которые он производит.
Дублированный код чаще всего устраняется путем перемещения кода в отдельный модуль ( функцию или модуль) и вызова этого модуля из всех мест, где он изначально использовался. Использование более открытого стиля разработки, при котором компоненты находятся в централизованном расположении, также может помочь в дублировании.
Код, включающий дублирующиеся функции, труднее поддерживать, потому что:
С другой стороны, если одна копия кода используется для разных целей и она не документирована должным образом, существует опасность, что она будет обновляться для одной цели, но это обновление не будет обязательным или соответствующим другой. целей.
Эти соображения не актуальны для автоматически генерируемого кода, если в исходном коде имеется только одна копия функциональности.
Раньше, когда объем памяти был более ограничен, дублирующийся код имел дополнительный недостаток: он занимал больше места, но сегодня это вряд ли станет проблемой.
При копировании кода с уязвимостью программного обеспечения уязвимость может продолжать существовать в скопированном коде, если разработчик не знает о таких копиях. [3] Рефакторинг дублированного кода может улучшить многие показатели программного обеспечения, такие как количество строк кода , цикломатическая сложность и связанность . Это может привести к сокращению времени компиляции, снижению когнитивной нагрузки , уменьшению количества человеческих ошибок и меньшему количеству забытых или пропущенных фрагментов кода. Однако не все дублирования кода можно рефакторить. [4] Клоны могут быть наиболее эффективным решением, если язык программирования предоставляет неадекватные или слишком сложные абстракции, особенно если он поддерживается такими методами пользовательского интерфейса, как одновременное редактирование . Более того, риски взлома кода при рефакторинге могут перевесить любые преимущества от обслуживания.[5] Исследование Вагнера, Абдулхалека и Кайи пришло к выводу, что, хотя для синхронизации дубликатов необходимо проделать дополнительную работу, если участвующие программисты знают о дублирующемся коде, то ошибок возникает не значительно больше, чем в недублированном коде.[6] [ оспаривается ]
Для обнаружения дублированного кода был предложен ряд различных алгоритмов. Например:
Рассмотрим следующий фрагмент кода для вычисления среднего значения массива целых чисел .
внешний int array_a []; внешний int array_b []; интервал sum_a = 0 ; for ( int i = 0 ; я < 4 ; я ++ ) sum_a += array_a [ i ]; int Average_a = sum_a / 4 ; интервал sum_b = 0 ; for ( int i = 0 ; я < 4 ; я ++ ) sum_b += array_b [ i ]; int Average_b = sum_b / 4 ;
Два цикла можно переписать как одну функцию:
intcalc_average_of_four ( int * array ) { int sum = 0 ; _ for ( int i = 0 ; i < 4 ; я ++ ) sum += массив [ i ]; сумма возврата / 4 ; }
или, что обычно предпочтительнее, путем параметризации количества элементов в массиве.
Использование вышеуказанной функции даст исходный код, в котором нет дублирования циклов:
внешний массив int1 []; внешний int array2 []; int Average1 = Calc_average_of_four ( массив1 ); int среднее2 = Calc_average_of_four ( массив2 );
Обратите внимание, что в этом тривиальном случае компилятор может выбрать встраивание обоих вызовов функции, так что результирующий машинный код будет идентичен как для дублированных, так и для недублированных примеров выше. Если функция не встроена, то дополнительные затраты на вызовы функций, вероятно, займут больше времени (порядка 10 инструкций процессора для большинства высокопроизводительных языков). Теоретически, это дополнительное время для бега может иметь значение.