Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Regla 0x2015h: No dupliques lógica: extraé una función

Funciones, contratos y modularizacion (0x20XX)

Universidad Nacional de Río Negro

0x2015h: No dupliques lógica: extraé una función

Enunciado normativo

Si el mismo bloque de lógica (aunque sea con identifiers distintos) aparece dos o más veces, DEBE extraerse a una función con nombre propio y ser invocado desde cada lugar. NO DEBE copiarse y pegarse.

¿Por qué existe esta regla?

El problema

El código duplicado es una promesa de inconsistencia futura. Cuando el requisito cambie, habrá que localizar todas las copias y modificarlas igual; es inevitable olvidar alguna. Y el error que se corrige en una copia sobrevive en las otras.

Además, la lógica duplicada no tiene nombre, así que nadie puede reutilizarla ni probarla por separado. Al extraerla, gana un nombre que documenta su intención y un punto de prueba único.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Bug silenciosoUna copia se corrige y las otras no.
MantenibilidadCada cambio exige buscar y reemplazar en varios lugares.
TestingHay que probar cada copia por separado.
TamañoLa función que contiene las copias crece y se vuelve ilegible.

Fundamento en la cátedra

Es el principio DRY (Don’t Repeat Yourself) aplicado a la escala del curso. Se apoya en 0x2008h: Los ejercicios deben ser resueltos mediante funciones, que ya exige resolver mediante funciones, y en 0x2005h: Cada función debe tener una única responsabilidad (Principio de Responsabilidad Única), que pide una responsabilidad por función.

Alcance y excepciones

Dos bloques idénticos son siempre candidatos. Dos bloques parecidos pero con evolución divergente conocida pueden justificar no unificarlos, para no forzar una abstracción prematura. En el curso, ante la duda, extraer.

Ejemplos exhaustivos

❌ Contraejemplo 1 — Validación copiada

if (edad >= 0 && edad <= 120) {
    /* ... */
}

/* ... cincuenta líneas después ... */
if (edad2 >= 0 && edad2 <= 120) {
    /* ... */
}

/* ... y en otra función ... */
if (otra_edad < 0 || otra_edad > 120) {
    /* error */
}

Por qué falla: la misma regla de negocio (rango válido de edad) aparece tres veces; si mañana el máximo cambia a 110, es casi seguro que una copia quede desactualizada.

❌ Contraejemplo 2 — Cálculo repetido

double area1 = 3.14159 * r1 * r1;
double longitud1 = 2 * 3.14159 * r1;

/* ... */
double area2 = 3.14159 * r2 * r2;
double longitud2 = 2 * 3.14159 * r2;

Por qué falla: la constante y la fórmula se repiten; un cambio de precisión o de fórmula obliga a editar todas.

✅ Ejemplo conforme 1 — Función con nombre

static bool edad_valida(int edad)
{
    return edad >= 0 && edad <= 120;
}
if (edad_valida(edad)) {
    /* ... */
}

La regla vive en un único lugar, tiene nombre y se puede probar con assert.

✅ Ejemplo conforme 2 — Funciones de dominio

static double area_circulo(double radio)
{
    return PI * radio * radio;
}

static double longitud_circulo(double radio)
{
    return 2.0 * PI * radio;
}

La constante PI se define una vez (0x0112h: Usá constantes simbólicas para todo literal con significado) y las fórmulas quedan disponibles para todo el programa.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
weyl / dredddetección de plagio / similitudBloques internos repetidos en el mismo archivo.
gigeranálisis de call graphFunciones grandes con sub-bloques idénticos.
Revisión manualBuscar el mismo if o la misma fórmula dos veces.

Checklist de autocontrol

Reglas relacionadas

Antipatrón: Ramas idénticas duplicadas en bifurcación if-else

Síntoma en el código del estudiante

Ambas ramas de un if / else contienen exactamente la misma sentencia:

if (x > 0)
{
    total += x;
}
else
{
    total += x;
}

Diagnóstico

Mecanismo del defecto

Si las dos ramas tienen el mismo cuerpo, la condición no discrimina: el resultado es idéntico sin importar su valor. La estructura de control queda redundante. El caso típico es un error de copiado al preparar la rama alterna, o el residuo de una bifurcación que perdió sentido tras un refactor.

Consecuencia observable

El programa funciona, pero la intención no: el lector busca en vano la diferencia entre las ramas y sospecha de un bug. Si una de las ramas debía hacer otra cosa (por ejemplo, restar en lugar de sumar), el defecto es un bug lógico silencioso que ninguna herramienta señala por defecto.

Fundamento en el estándar C11

ISO/IEC 9899:2011 §6.8.4.1 permite cualquier par de sentencias en las ramas del if; no es un error sintáctico. La cátedra lo trata como code smell y pide eliminar la condición o corregir la rama divergente. Se relaciona con 0x2005h: Cada función debe tener una única responsabilidad (Principio de Responsabilidad Única) (responsabilidad única) y con 0x1004h: Las condiciones complejas deben simplificarse o comentarse (condiciones con intención).

Corrección idiomática

❌ Código con el antipatrón
if (x > 0)
{
    total += x;
}
else
{
    total += x;
}
✅ Código refactorizado
total += x;

Si la rama alterna debía hacer otra cosa, se corrige:

if (x > 0)
{
    total += x;
}
else
{
    total -= x;
}

La condición recupera su propósito: cada rama hace algo distinto y el if deja de ser decorativo.

Errores típicos al compilar o ejecutar

No hay advertencia del compilador por defecto. Con -Wall y optimización,
el compilador puede detectar ramas idénticas y colapsarlas:
warning: this 'if' clause does not guard... [-Wmisleading-indentation]
solo en variantes específicas; no es una detección garantizada.

El bug es lógico: si una rama debía restar, el total nunca decrece.

Checklist de verificación

Reglas relacionadas