0x7007h: Evitá los parámetros bandera de tipo bool¶
Enunciado normativo¶
NO DEBE usarse un parámetro de tipo
boolpara seleccionar entre dos comportamientos distintos de una función. Si una función hace una cosa u otra según una bandera, DEBEN definirse dos funciones con nombres que describan cada comportamiento.
¿Por qué existe esta regla?¶
El problema¶
Una función con un parámetro bandera es, en realidad, dos funciones
fusionadas. El nombre del parámetro no aparece en la llamada, de modo que
imprimir(lista, true) no dice qué hará el true. El llamador debe recordar
la convención o consultar la firma, y con varios booleanos la combinación se
vuelve inmanejable.
Separar en dos funciones con nombres verbales elimina la ambigüedad y hace que cada una tenga una única responsabilidad (ver 0x2014h: Cada función debe caber en una sola idea y en 25 líneas).
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Llamadas ilegibles | procesar(x, true, false) es indescifrable. |
| Testing | Hay que preparar el contexto para ambas ramas en cada prueba. |
| Mantenibilidad | Agregar un comportamiento exige otra bandera y más if. |
| Contratos | La función tiene dos contratos incompatibles en una firma. |
Fundamento en la cátedra¶
Es una aplicación directa de 0x2005h: Cada función debe tener una única responsabilidad (Principio de Responsabilidad Única) (responsabilidad única) y de 0x2014h: Cada función debe caber en una sola idea y en 25 líneas (una función, una idea). La cátedra prefiere nombres de dominio antes que banderas crípticas.
Alcance y excepciones¶
Se exceptúan las funciones cuya bandera no cambia el comportamiento sino
un detalle documentado y evidente por el nombre del parámetro en la firma
(por ejemplo, ordenar(v, n, /* ascendente */ true) cuando existe un
enum o comentario de argumento). La excepción es estrecha; en el curso se
prefiere casi siempre separar.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Bandera que selecciona el camino¶
void imprimir(int v[], size_t n, bool con_indice)
{
for (size_t i = 0; i < n; i++) {
if (con_indice) {
printf("%zu: %d\n", i, v[i]);
} else {
printf("%d\n", v[i]);
}
}
}Por qué falla: la llamada imprimir(v, n, true) no comunica nada; el lector
no sabe si true significa “con índice” o “en reversa”.
❌ Contraejemplo 2 — Múltiples banderas¶
procesar(datos, true, false, true);Por qué falla: tres booleanos consecutivos son imposibles de recordar; es la máxima expresión del antipatrón.
✅ Ejemplo conforme 1 — Dos funciones con nombre¶
static void imprimir_valores(const int v[], size_t n)
{
for (size_t i = 0; i < n; i++) {
printf("%d\n", v[i]);
}
}
static void imprimir_indizado(const int v[], size_t n)
{
for (size_t i = 0; i < n; i++) {
printf("%zu: %d\n", i, v[i]);
}
}Cada llamada dice exactamente qué hace; no hay bandera que recordar.
✅ Ejemplo conforme 2 — enum cuando el modo es un dominio¶
typedef enum {
ORDEN_ASCENDENTE,
ORDEN_DESCENDENTE
} orden_t;
void ordenar(int v[], size_t n, orden_t orden);Si el modo es un dominio con nombre, un enum es preferible a un bool
porque la llamada se lee ordenar(v, n, ORDEN_DESCENDENTE) y el compilador
valida el valor.
⚠️ Casos límite¶
Bandera de depuración:
procesar(x, /* verbose */ true)puede justificarse si se documenta y el comportamiento extra es sólo informativo, no una bifurcación de contrato.Compatibilidad con bibliotecas: funciones estándar pueden tener parámetros de modo; no se editan.
Diseño de API: si se descubre una bandera, reconsiderar si no hay dos operaciones o un
enumde dominio.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
| Revisión manual | — | Firmas con bool y un if (bandera) en el cuerpo. |
gaff | gaff check archivo.c | Regla 0x2005h (responsabilidad única). |
Checklist de autocontrol¶
¿Mi función tiene un parámetro
boolque cambia su comportamiento?¿Puedo nombrar las dos funciones resultantes?
¿La llamada se entiende sin consultar la firma?
¿Un
enumde dominio modela mejor el modo?
Reglas relacionadas¶
0x2015h: No dupliques lógica: extraé una función — extraer funciones al separar la bandera.
0x2005h: Cada función debe tener una única responsabilidad (Principio de Responsabilidad Única) — responsabilidad única.
0x1005h: Reemplazá las condiciones ambiguas basadas en la ‘veracidad’ (truthiness) del tipo de dato — comparación explícita de booleanos.
0x2016h: Escribí el contrato de la función antes de implementarla — un contrato por función.