Regla 0x2005h: Cada función debe tener una única responsabilidad (Principio de Responsabilidad Única)
Funciones, contratos y modularizacion (0x20XX)
0x2005h: Cada función debe tener una única responsabilidad (Principio de Responsabilidad Única)¶
Enunciado normativo¶
DEBE poder describirse la responsabilidad de cada función con una sola frase sin conjunciones. Si la descripción necesita un “y” (o un “además”), la función DEBE dividirse en funciones separadas.
¿Por qué existe esta regla?¶
El problema¶
Una función con dos responsabilidades tiene dos motivos para cambiar y dos conjuntos de pruebas; el lector no puede reutilizar la mitad que necesita porque las partes comparten variables y flujo. Cada responsabilidad extra multiplica los caminos. La unidad de medida no son las líneas sino las razones de cambio: “leer, validar y guardar” hace tres cosas que evolucionan aparte.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Bug silencioso | Cambiar la validación altera sin querer la parte de guardado. |
| Reutilización | La lógica de cálculo queda inaccesible por estar mezclada con E/S. |
Fundamento en el estándar y en la cátedra¶
El Principio de Responsabilidad Única proviene del diseño de software; en C11 se apoya en la función como unidad de compilación y prueba (§6.9.1). La cátedra lo adopta como eje del bloque 0x20XX (0x2002h: Las funciones no deben contener printf o scanf, a menos que ese sea su propósito explícito, 0x200Ah: Modularización: una función no debe exceder 4 parámetros de entrada, 0x2014h: Cada función debe caber en una sola idea y en 25 líneas).
Alcance y excepciones¶
Aplica a toda función con lógica. Una preparación seguida de un cálculo sigue
siendo una sola responsabilidad si comparten una idea. El olor se detecta
cuando el nombre necesita “y”, cuando la mitad del cuerpo se puede probar sin
la otra o cuando hay parámetros bandera. Excepción: main es el
orquestador y puede encadenar pasos que delegan en funciones.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Leer, validar y promediar en un solo cuerpo¶
void procesar_edades(void)
{
int edades[100];
int n = 0;
printf("Ingrese edades (-1 para terminar):\n");
int x;
while (n < 100 && scanf("%d", &x) == 1 && x != -1) {
if (x >= 0 && x <= 120) {
edades[n] = x;
n++;
}
}
double suma = 0.0;
for (int i = 0; i < n; i++) {
suma += edades[i];
}
printf("Promedio: %.2f\n", suma / n);
}Hace entrada, validación, acumulación e impresión, y divide por cero si n es 0.
❌ Contraejemplo 2 — Parámetro bandera que esconde tres funciones¶
int calcular(int a, int b, int modo)
{
if (modo == 1) {
return a + b;
} else if (modo == 2) {
return a - b;
} else {
return a * b;
}
}modo elige entre tres operaciones: la función es tres disfrazadas y su nombre
no describe ninguna (0x2009h: Los nombres de funciones y procedimientos deben usar snake_case en minúsculas). Además viola 0x2013h: Detector de bloques else superfluos tras sentencias terminales.
✅ Ejemplo conforme 1 — Cada responsabilidad en su función¶
static bool edad_valida(int edad)
{
return edad >= 0 && edad <= 120;
}
static double promedio(const int edades[], size_t n)
{
if (n == 0) {
return 0.0;
}
long suma = 0;
for (size_t i = 0; i < n; i++) {
suma += edades[i];
}
return (double)suma / (double)n;
}edad_valida responde una pregunta y promedio calcula una cosa, manejando el
caso vacío. Ambas se prueban con un assert aislado.
✅ Ejemplo conforme 2 — Interfaz y cálculo separados¶
static void mostrar_promedio(double valor)
{
printf("Promedio: %.2f\n", valor);
}
int main(void)
{
int edades[] = {20, 30, 40};
mostrar_promedio(promedio(edades, 3));
return 0;
}Cada paso cambia por un motivo distinto: uno por la fórmula, otro por el formato de pantalla (0x2002h: Las funciones no deben contener printf o scanf, a menos que ese sea su propósito explícito).
⚠️ Casos límite¶
Funciones muy cortas: extraer un
get/setde una línea no aporta responsabilidad; dividir de más también es un olor.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Función que supera el umbral de tamaño o anidación. |
giger | análisis de call graph | Funciones que concentran llamadas heterogéneas. |
Checklist de autocontrol¶
¿Puedo describir la función con una frase sin “y”?
Reglas relacionadas¶
0x2002h: Las funciones no deben contener printf o scanf, a menos que ese sea su propósito explícito — separar cálculo de E/S es un caso de responsabilidad única.
0x200Ah: Modularización: una función no debe exceder 4 parámetros de entrada — demasiados parámetros suele delatar varias responsabilidades.