En programación de computadoras , el código duplicado es una secuencia de código fuente que ocurre más de una vez, ya sea dentro de un programa o en diferentes programas que pertenecen o son mantenidos por la misma entidad. El código duplicado generalmente se considera indeseable por varias razones. [1] Generalmente se aplica un requisito mínimo a la cantidad de código que debe aparecer en una secuencia para que se considere duplicada en lugar de coincidentemente similar. Las secuencias de código duplicado a veces se conocen como clones de código o simplemente clones; el proceso automatizado de encontrar duplicaciones en el código fuente se llama detección de clones.
Dos secuencias de códigos pueden ser duplicados entre sí sin ser idénticas carácter por carácter, por ejemplo siendo idénticas carácter por carácter sólo cuando se ignoran los espacios en blanco y los comentarios, o siendo idénticas token por token , o token -for-token idéntico con variación ocasional. Incluso las secuencias de código que sólo son funcionalmente idénticas pueden considerarse código duplicado.
Algunas de las formas en que se puede crear código duplicado son:
También puede suceder que se requiera una funcionalidad muy similar a la de otra parte de un programa, y un desarrollador escriba de forma independiente código muy similar a lo que existe en otros lugares. Los estudios sugieren que este tipo de código reescrito de forma independiente no suele ser sintácticamente similar. [2]
El código generado automáticamente, donde es posible que desee tener código duplicado para aumentar la velocidad o la facilidad de desarrollo, es otra razón para la duplicación. Tenga en cuenta que el generador real no contendrá duplicados en su código fuente, solo la salida que produce.
El código duplicado se soluciona más comúnmente moviendo el código a su propia unidad ( función o módulo) y llamando a esa unidad desde todos los lugares donde se usó originalmente. El uso de un estilo de desarrollo más abierto, en el que los componentes se encuentran en ubicaciones centralizadas, también puede ayudar con la duplicación.
El código que incluye funcionalidad duplicada es más difícil de soportar porque,
Por otro lado, si una copia del código se utiliza para diferentes propósitos y no está debidamente documentada, existe el peligro de que se actualice para un propósito, pero esta actualización no será necesaria ni apropiada para el otro. propósitos.
Estas consideraciones no son relevantes para el código generado automáticamente, si solo hay una copia de la funcionalidad en el código fuente.
En el pasado, cuando el espacio de la memoria era más limitado, el código duplicado tenía la desventaja adicional de ocupar más espacio, pero hoy en día es poco probable que esto sea un problema.
Cuando se copia un código con una vulnerabilidad de software , la vulnerabilidad puede continuar existiendo en el código copiado si el desarrollador no tiene conocimiento de dichas copias. [3] La refactorización de código duplicado puede mejorar muchas métricas de software, como líneas de código , complejidad ciclomática y acoplamiento . Esto puede conducir a tiempos de compilación más cortos, menor carga cognitiva , menos errores humanos y menos fragmentos de código olvidados o pasados por alto. Sin embargo, no todos los códigos duplicados se pueden refactorizar. [4] Los clones pueden ser la solución más eficaz si el lenguaje de programación proporciona abstracciones inadecuadas o demasiado complejas, especialmente si se soportan con técnicas de interfaz de usuario como la edición simultánea . Además, los riesgos de romper el código durante la refactorización pueden superar cualquier beneficio de mantenimiento. [5] Un estudio realizado por Wagner, Abdulkhaleq y Kaya concluyó que si bien se debe realizar trabajo adicional para mantener sincronizados los duplicados, si los programadores involucrados son conscientes del código duplicado, no se produjeron significativamente más fallas que en el código no duplicado. [6] [ disputado ]
Se han propuesto varios algoritmos diferentes para detectar código duplicado. Por ejemplo:
Considere el siguiente fragmento de código para calcular el promedio de una matriz de números enteros
externo int array_a []; externo int array_b []; int suma_a = 0 ; para ( int i = 0 ; i < 4 ; i ++ ) sum_a += array_a [ i ]; int promedio_a = suma_a / 4 ; int suma_b = 0 ; para ( int i = 0 ; i < 4 ; i ++ ) sum_b += array_b [ i ]; int promedio_b = suma_b / 4 ;
Los dos bucles se pueden reescribir como una única función:
int calc_average_of_four ( int * matriz ) { int suma = 0 ; para ( int i = 0 ; i < 4 ; i ++ ) suma += matriz [ i ]; devolver suma / 4 ; }
o, normalmente preferentemente, parametrizando el número de elementos de la matriz.
El uso de la función anterior proporcionará un código fuente que no tiene duplicación de bucle:
externo int matriz1 []; externo int array2 []; int promedio1 = calc_promedio_de_cuatro ( matriz1 ); int promedio2 = calc_promedio_de_cuatro ( matriz2 );
Tenga en cuenta que en este caso trivial, el compilador puede optar por incorporar ambas llamadas a la función, de modo que el código de máquina resultante sea idéntico para los ejemplos duplicados y no duplicados anteriores. Si la función no está integrada, entonces la sobrecarga adicional de las llamadas a la función probablemente tardará más en ejecutarse (del orden de 10 instrucciones de procesador para la mayoría de los lenguajes de alto rendimiento). En teoría, este tiempo adicional para correr podría ser importante.