Проблема дублирования исходного кода в программных продуктах

№1-1,

технические науки

Авторская (единоличная) разработка достаточно большого и сложного программного продукта практически неосуществима. Рано или поздно возникает необходимость разделить, распараллелить часть задач между исполнителями, чтобы эффективно и своевременно развивать и поддерживать продукт. Однако увеличение числа исполнителей вносит ряд определённых проблем. Как правило, разработчики слабо взаимодействуют друг с другом при выполнении мелких повседневных задач – необходимые им функции они могут реализовывать независимо от остальной команды. Таким образом, возникают ситуации, когда один и тот же по назначению программный код присутствует в проекте несколько раз, то есть дублируется.

Похожие материалы

Авторская (единоличная) разработка достаточно большого и сложного программного продукта практически неосуществима. Рано или поздно возникает необходимость разделить, распараллелить часть задач между исполнителями, чтобы эффективно и своевременно развивать и поддерживать продукт. Однако увеличение числа исполнителей вносит ряд определённых проблем. Как правило, разработчики слабо взаимодействуют друг с другом при выполнении мелких повседневных задач – необходимые им функции они могут реализовывать независимо от остальной команды. Таким образом, возникают ситуации, когда один и тот же по назначению программный код присутствует в проекте несколько раз, то есть дублируется.

Какой именно код считать дублирующимся? Есть несколько вариантов ответов на этот вопрос. Согласно Бакстер (Baxter) [3] дубликаты кода, называемые в её работе клонами (software clones), это «фрагменты программы, идентичные другим фрагментам». Кринке (Krinke) использует для этого термин «сходный код» (similarcode); Дукасси (Ducasse) говорит о дублирующемся коде (duplicatedcode), Комондор (Komondoor) и Хорвиц (Horwitz) также используют термин «дублирующийся код» (duplicatedcode), а клонами (clones) они называют отдельные экземпляры дубликатов кода.

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

Дублирование программного кода имеет несколько форм.

  1. Текстовое дублирование. Этот вид, пожалуй, наиболее простой и легко находимый из остальных. Он проявляется, когда в программе есть секции кода, которые идентичны или очень похожи. Есть несколько способов избежать этого вида дублирования, включая: выделение методов (extracting methods) и повторное их использование (reusing), превращение метода в шаблонный метод (template method) и другие базовые виды рефакторинга.
  2. Функциональное дублирование.Это особый вид текстового дублирования, когда в исходном коде есть различные функции или методы, предназначенные  для одного и того же. Его можно часто встретить в проектах, которые разделены между группами разработчиков, недостаточно хорошо взаимодействующих друг с другом.
  3. Временное дублирование.Оно немного более трудное, менее ясное, и не так легко обнаруживаемое. Временное дублирование наблюдается, когда при выполнении программы одна и та же работа делается повторно – в то время, когда так не должно быть. Это не проблема в том же понимании, как предыдущие две формы дублирования, так как это не приводит к наличию лишнего кода. Но это может стать проблемой при масштабировании системы, когда повторяющаяся работа начинает отнимать много времени.

В дальнейшем мы будем рассматривать только текстовое и, отчасти, функциональное дублирование.

В литературе приводится следующая типизация клонов (табл.1.1).

Таблица 1.1

Классификация дублирующихся блоков программного кода

Название типа

Описание

Тип 1

Абсолютно идентичный фрагмент кода, не содержащий никаких различий

Тип 2

Идентичный фрагмент кода, но отформатированный, содержащий комментарии, или изменённые имена переменных (рис.1.1)

Тип 3

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

Тип 4

Функционально идентичный фрагмент кода, вероятно созданный автором, не подозревающим о существовании оригинального кода (рис.1.3)

 

 

RS-Bank5.50.001.30.10\BFCLN.ALL\dub_clnt.c

 

// функция высвобождения

static void CL_ClearFunc( void * item )

{

  if ( item ) free ( item );

}

RS-Bank5.50.001.30.10\BFCLN.ALL\dub_grp.c

 

// функция высвобождения

void DBGRP_ClearFunc( void * item )

{

  if ( item ) free ( item );

}

Рис.1.1 – Пример клонов 2-го типа в АБС RS-Bank\Pervasive

 

File: linux-2.6.19/drivers/scsi/arm/eesox.c

 

407: if (length >= 9 && strncmp (buffer, "EESOXSCSI", 9) == 0) {

408: buffer += 9;

409: length -= 9;

410:

411: if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {

......

418: } else

419: ret = -EINVAL;

420: } else

421: ret = -EINVAL;

 

File: linux-2.6.19/drivers/scsi/arm/cumana 2.c

 

322: if (length >= 11 && strcmp (buffer, "CUMANASCSI2") == 0) {

323: buffer += 11;

324: length -= 11;

325:

326: if (length >= 5 && strncmp(buffer, "term=", 5) == 0) {

......

333: } else

334: ret = -EINVAL;

335: } else

336: ret = -EINVAL;

Рис.1.2 – Пример клонов 3-го типа в ядре Linux

File: linux-2.6.19/drivers/cdrom/sbpcd.c

 

4859: if (cmd_type==READ_M2)

4860: {

4861: for (xa_count=0;xa_count<CD_XA_HEAD;

      xa_count++)

4862: sprintf(&msgbuf[xa_count*3], " %02X",     

      ...);

4863: msgbuf[xa_count*3]=0;

4864: msg(DBG_XA1,"xa head:%s\n", msgbuf);

4865: }

 

File: linux-2.6.19/drivers/cdrom/sbpcd.c

 

2386: for (i=0;i<response_count;i++)

2387: {

2388: sprintf(&msgbuf[i*3], " %02X", ...);

2389: msgbuf[i*3]=0;

2390: msg(DBG_SQ1,"cc_ReadSubQ:%s\n",

      msgbuf);

2391: }

 

 

 

Рис.1.3 – Пример клонов 4-го типа в ядре Linux

«Двойной» код (duplicate code) может появляться и другими способами, например, когда программист берёт уже имеющийся код, копирует и видоизменяет его, достигая нужных целей. Повторное использование кода посредством копирования и вставки (copy/paste) является широко распространенной практикой программирования во избежание нежелательных эффектов, когда программист не хочет (или не может) предвидеть всех последствий от модификации имеющегося кода. Существенную роль здесь играют и текстовые редакторы, которые традиционно содержат функции для копирования и вставки текста. В больших системах такой подход вполне может стать стандартным способом построения новых модулей. Однако, как показано в [2] и [3], такой подход является выигрышным лишь в ближайшем рассмотрении. Все преимущества данной стратегии исчезают, когда возникает необходимость внесения изменений во все многочисленные копии, если оригинальный код требует правки или изменения. В некоторых случаях удаление клонов становится затруднительным (или даже невозможным) из-за связанных рисков, поскольку сложно предсказать, как это скажется на качестве программного продукта.

В качестве примера в [3] приводится разработка драйверов устройств для различных операционных систем, когда значительная часть кода является, по сути, шаблоном, и только небольшая его часть, непосредственно взаимодействующая с аппаратной частью устройства, нуждается  в изменении при переходе от одной системы к другой. Как ни удивительно, но эта, казалось бы, хорошая практика повторного использования кода, только усугубляет проблемы технической поддержки программного обеспечения, поскольку впоследствии ошибки, обнаруженные в «проверенной» части кода, необходимо исправлять и во всех новых версиях драйвера.

Помимо вышеуказанных причин появления дубликатов кода в [3] приводятся:

  1. стиль программирования;
  2. реализация точных вычислений;
  3. неспособность выделять (или использовать) абстрактные типы данных;
  4. повышение производительности;
  5. случайные ситуации (accidents).

Существование некоторых клонов оправдано соображениями производительности. Системы с жесткими временными ограничениями иногда оптимизируются вручную путём дублирования часто вызываемого кода, особенно когда компилятор не предлагает встраивания (in-lining) случайных выражений или вычислений. [2,3]

И, наконец, бывают ситуации, когда фрагменты кода случайно совпадают, хотя на самом деле не являются клонами. Глубокий анализ позволяет установить, что эти видимые дубликаты (apparent clones) на самом деле выполняют различные вычисления. К счастью, при увеличении размера фрагментов кода, количество таких мнимых клонов (accidental clones) значительно сокращается.

За исключением случайных клонов (accidental clones), присутствие дубликатов в коде приводит к необоснованному увеличению его объёма, что в свою очередь вынуждает программистов контролировать и отлаживать больше кода, чем нужно. Кроме того, возрастает время на компиляцию проекта. Следствием этого становятся увеличивающиеся затраты на поддержку и развитие программного продукта, и в целом, как правило, суммарное снижение его качества.

По разным оценкам [1], количество дубликатов в больших проектах варьируется в пределах от 5% до 50%. В связи с этим возникает задача своевременного обнаружения дублирующегося кода (detecting duplicated code) и его последующего удаления. На настоящий момент предложен ряд методов для выявления клонов, а часть их них получила практическую реализацию. Их рассмотрение и сравнительный анализ станут следующим этапом работы.

Список литературы

  1. Baker, B. A program for identifying duplicated code / B. Baker // Computing Science and Statistics. – 1992. – №24. – p. 49-57
  2. Koschke, R. Clone Detection Using Abstract Syntax Suffix Trees/ R. Koschke, R. Falke, P. Frenzel // Proceedings of the 13th Working Conference on Reverse Engineering. – 2006. – p. 253-262
  3. Baxter, I. Clone detection using abstract syntax trees / I. Baxter, A. Yahin, L. Moura. // In ICSM ’98: Proceedings of the International Conference on Software Maintenance. – USA, Washington, DC. – 1998.– p. 368