Introducción¶
Los condicionales son una parte fundamental de la programación, pero también son una fuente común de complejidad y errores. Un código con lógica condicional compleja es difícil de leer, mantener y testear. La refactorización de condicionales busca hacer la lógica más clara, expresiva y mantenible.
Este apunte presenta técnicas sistemáticas para simplificar expresiones booleanas, reducir anidamiento, y hacer que las condiciones sean auto-explicativas.
Problemas Comunes con Condicionales¶
1. Anidamiento Excesivo¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18// Problemático: pirámide de la perdición if (usuario != NULL) { if (usuario->activo) { if (usuario->edad >= 18) { if (usuario->saldo > 0) { procesar_compra(usuario); } else { printf("Saldo insuficiente\n"); } } else { printf("Usuario menor de edad\n"); } } else { printf("Usuario inactivo\n"); } } else { printf("Usuario inválido\n"); }
2. Condiciones Complejas¶
1 2 3 4 5 6 7 8// Difícil de entender de un vistazo if ((estado == ACTIVO || estado == PENDIENTE) && (tipo != TEMPORAL && tipo != PRUEBA) && (saldo > 1000 || credito_disponible > 500) && !(bloqueado || suspendido)) { // ... }
3. Lógica Duplicada¶
1 2 3 4 5 6 7 8if (edad >= 18 && edad <= 65 && !jubilado) { precio = PRECIO_ADULTO; } // Más adelante en el código... if (edad >= 18 && edad <= 65 && !jubilado) { aplicar_descuento(); }
4. Booleanos Implícitos¶
1 2 3 4 5 6 7 8 9 10// Redundante if (es_valido() == true) { // ... } if (contador > 0) { return true; } else { return false; }
Técnicas de Refactorización¶
1. Guardia de Cláusulas (Guard Clauses)¶
Invertir condiciones para manejar casos especiales temprano y reducir anidamiento.
Antes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22void procesar_pedido(pedido_t* pedido) { if (pedido != NULL) { if (pedido->items_count > 0) { if (pedido->cliente != NULL) { if (pedido->cliente->saldo >= pedido->total) { // Lógica principal de procesamiento realizar_cargo(pedido); actualizar_inventario(pedido); enviar_confirmacion(pedido); } else { printf("Saldo insuficiente\n"); } } else { printf("Cliente inválido\n"); } } else { printf("Pedido vacío\n"); } } else { printf("Pedido nulo\n"); } }
Después:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27void procesar_pedido(pedido_t* pedido) { // Guardia de cláusulas - validaciones tempranas if (pedido == NULL) { printf("Pedido nulo\n"); return; } if (pedido->items_count == 0) { printf("Pedido vacío\n"); return; } if (pedido->cliente == NULL) { printf("Cliente inválido\n"); return; } if (pedido->cliente->saldo < pedido->total) { printf("Saldo insuficiente\n"); return; } // Lógica principal ahora está al mismo nivel realizar_cargo(pedido); actualizar_inventario(pedido); enviar_confirmacion(pedido); }
Beneficios:
Código más plano y fácil de leer
Casos de error manejados temprano
Lógica principal clara y sin anidamiento
Siguiendo el patrón de único retorno, podríamos usar
gotopara cleanup centralizado
2. Extracción de Condiciones a Variables Booleanas¶
Antes:
if (usuario->edad >= 18 && usuario->edad <= 65 &&
!usuario->jubilado && usuario->activo &&
(usuario->tipo == REGULAR || usuario->tipo == PREMIUM)) {
aplicar_beneficio(usuario);
}Después:
1 2 3 4 5 6 7 8 9 10 11bool es_adulto_en_edad_laboral = usuario->edad >= 18 && usuario->edad <= 65 && !usuario->jubilado; bool es_usuario_activo_valido = usuario->activo && (usuario->tipo == REGULAR || usuario->tipo == PREMIUM); if (es_adulto_en_edad_laboral && es_usuario_activo_valido) { aplicar_beneficio(usuario); }
Beneficios:
Condiciones autodocumentadas
Reutilizables
Más fáciles de testear individualmente
3. Extracción de Condiciones a Funciones¶
Para lógica compleja o reutilizable:
Antes:
1 2 3 4 5 6 7void procesar_descuento(cliente_t* cliente, double total) { if ((cliente->compras_totales > 10000 && cliente->antiguedad > 365) || (cliente->referidos >= 5) || (cliente->tipo == VIP && cliente->activo)) { aplicar_descuento_premium(total); } }
Después:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24bool es_cliente_fiel(const cliente_t* cliente) { return cliente->compras_totales > 10000 && cliente->antiguedad > 365; } bool es_buen_referidor(const cliente_t* cliente) { return cliente->referidos >= 5; } bool es_vip_activo(const cliente_t* cliente) { return cliente->tipo == VIP && cliente->activo; } bool califica_para_descuento_premium(const cliente_t* cliente) { return es_cliente_fiel(cliente) || es_buen_referidor(cliente) || es_vip_activo(cliente); } void procesar_descuento(cliente_t* cliente, double total) { if (califica_para_descuento_premium(cliente)) { aplicar_descuento_premium(total); } }
Beneficios:
Nombres descriptivos documentan la lógica de negocio
Funciones testeables independientemente
Reutilizables en otros contextos
Fácil modificar criterios de elegibilidad
4. Simplificación de Booleanos¶
Antes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16bool es_valido(int valor) { if (valor > 0 && valor < 100) { return true; } else { return false; } } // Comparación redundante if (esta_activo() == true) { // ... } if (contador > 0 == false) { // ... }
Después:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16bool es_valido(int valor) { return valor > 0 && valor < 100; } // Uso directo del booleano if (esta_activo()) { // ... } if (!esta_activo()) { // ... } if (contador == 0) { // ... }
5. Reemplazo de Condicionales con Polimorfismo (simulado en C)¶
Para casos donde múltiples if-else determinan comportamiento:
Antes:
1 2 3 4 5 6 7 8 9 10 11 12 13double calcular_area(figura_t* figura) { if (figura->tipo == CIRCULO) { return PI * figura->datos.circulo.radio * figura->datos.circulo.radio; } else if (figura->tipo == RECTANGULO) { return figura->datos.rectangulo.ancho * figura->datos.rectangulo.alto; } else if (figura->tipo == TRIANGULO) { return 0.5 * figura->datos.triangulo.base * figura->datos.triangulo.altura; } return 0; }
Después (con punteros a función):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26typedef double (*calcular_area_fn)(const void* datos); typedef struct { int tipo; calcular_area_fn calcular_area; void* datos; } figura_t; double calcular_area_circulo(const void* datos) { const circulo_t* c = (const circulo_t*)datos; return PI * c->radio * c->radio; } double calcular_area_rectangulo(const void* datos) { const rectangulo_t* r = (const rectangulo_t*)datos; return r->ancho * r->alto; } double calcular_area_triangulo(const void* datos) { const triangulo_t* t = (const triangulo_t*)datos; return 0.5 * t->base * t->altura; } double calcular_area(const figura_t* figura) { return figura->calcular_area(figura->datos); }
6. Tabla de Decisión¶
Para lógica compleja con múltiples combinaciones:
Antes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17char* obtener_categoria(int edad, bool estudiante, bool empleado) { if (edad < 18 && estudiante) { return "ESTUDIANTE_MENOR"; } else if (edad < 18 && !estudiante) { return "MENOR"; } else if (edad >= 18 && edad < 65 && estudiante) { return "ESTUDIANTE_ADULTO"; } else if (edad >= 18 && edad < 65 && empleado) { return "EMPLEADO"; } else if (edad >= 18 && edad < 65) { return "ADULTO"; } else if (edad >= 65 && empleado) { return "JUBILADO_ACTIVO"; } else { return "JUBILADO"; } }
Después (con tabla):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53typedef struct { bool (*condicion)(int edad, bool estudiante, bool empleado); const char* categoria; } regla_categoria_t; bool es_estudiante_menor(int edad, bool estudiante, bool empleado) { return edad < 18 && estudiante; } bool es_menor(int edad, bool estudiante, bool empleado) { return edad < 18 && !estudiante; } bool es_estudiante_adulto(int edad, bool estudiante, bool empleado) { return edad >= 18 && edad < 65 && estudiante; } bool es_empleado(int edad, bool estudiante, bool empleado) { return edad >= 18 && edad < 65 && empleado; } bool es_adulto(int edad, bool estudiante, bool empleado) { return edad >= 18 && edad < 65 && !estudiante && !empleado; } bool es_jubilado_activo(int edad, bool estudiante, bool empleado) { return edad >= 65 && empleado; } bool es_jubilado(int edad, bool estudiante, bool empleado) { return edad >= 65 && !empleado; } const regla_categoria_t REGLAS_CATEGORIA[] = { {es_estudiante_menor, "ESTUDIANTE_MENOR"}, {es_menor, "MENOR"}, {es_estudiante_adulto, "ESTUDIANTE_ADULTO"}, {es_empleado, "EMPLEADO"}, {es_adulto, "ADULTO"}, {es_jubilado_activo, "JUBILADO_ACTIVO"}, {es_jubilado, "JUBILADO"} }; const int NUM_REGLAS = sizeof(REGLAS_CATEGORIA) / sizeof(REGLAS_CATEGORIA[0]); const char* obtener_categoria(int edad, bool estudiante, bool empleado) { for (int i = 0; i < NUM_REGLAS; i++) { if (REGLAS_CATEGORIA[i].condicion(edad, estudiante, empleado)) { return REGLAS_CATEGORIA[i].categoria; } } return "DESCONOCIDO"; }
7. Uso de switch en lugar de if-else en cadena¶
Para comparaciones de igualdad con un valor:
Antes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15void procesar_comando(char comando) { if (comando == 'A') { avanzar(); } else if (comando == 'R') { retroceder(); } else if (comando == 'I') { girar_izquierda(); } else if (comando == 'D') { girar_derecha(); } else if (comando == 'P') { parar(); } else { printf("Comando inválido\n"); } }
Después:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22void procesar_comando(char comando) { switch (comando) { case 'A': avanzar(); break; case 'R': retroceder(); break; case 'I': girar_izquierda(); break; case 'D': girar_derecha(); break; case 'P': parar(); break; default: printf("Comando inválido\n"); break; } }
Casos Prácticos Completos¶
Caso 1: Validación de Formulario¶
Código Original:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43bool validar_formulario(const char* nombre, const char* email, const char* telefono, int edad) { if (nombre != NULL) { if (strlen(nombre) >= 3) { if (email != NULL) { if (strchr(email, '@') != NULL) { if (strlen(email) >= 5) { if (telefono != NULL) { if (strlen(telefono) >= 8) { if (edad >= 18) { if (edad <= 120) { return true; } else { printf("Edad muy alta\n"); } } else { printf("Debe ser mayor de edad\n"); } } else { printf("Teléfono muy corto\n"); } } else { printf("Teléfono nulo\n"); } } else { printf("Email muy corto\n"); } } else { printf("Email sin @\n"); } } else { printf("Email nulo\n"); } } else { printf("Nombre muy corto\n"); } } else { printf("Nombre nulo\n"); } return false; }
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70bool validar_nombre(const char* nombre) { if (nombre == NULL) { printf("Nombre nulo\n"); return false; } if (strlen(nombre) < 3) { printf("Nombre muy corto\n"); return false; } return true; } bool validar_email(const char* email) { if (email == NULL) { printf("Email nulo\n"); return false; } if (strlen(email) < 5) { printf("Email muy corto\n"); return false; } if (strchr(email, '@') == NULL) { printf("Email sin @\n"); return false; } return true; } bool validar_telefono(const char* telefono) { if (telefono == NULL) { printf("Teléfono nulo\n"); return false; } if (strlen(telefono) < 8) { printf("Teléfono muy corto\n"); return false; } return true; } bool validar_edad(int edad) { if (edad < 18) { printf("Debe ser mayor de edad\n"); return false; } if (edad > 120) { printf("Edad muy alta\n"); return false; } return true; } bool validar_formulario(const char* nombre, const char* email, const char* telefono, int edad) { return validar_nombre(nombre) && validar_email(email) && validar_telefono(telefono) && validar_edad(edad); }
Caso 2: Cálculo de Descuento Complejo¶
Código Original:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41double calcular_descuento(double monto, int cantidad, bool es_miembro, bool es_primera_compra, const char* codigo_promo) { double descuento = 0; if (cantidad > 10 && monto > 1000) { if (es_miembro) { if (strcmp(codigo_promo, "VERANO") == 0) { descuento = 0.30; } else if (strcmp(codigo_promo, "FLASH") == 0) { if (cantidad > 20) { descuento = 0.35; } else { descuento = 0.25; } } else { descuento = 0.20; } } else { if (es_primera_compra) { descuento = 0.15; } else { descuento = 0.10; } } } else if (cantidad > 5 || monto > 500) { if (es_miembro) { descuento = 0.15; } else { descuento = 0.05; } } else { if (es_primera_compra) { descuento = 0.10; } } return descuento; }
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43bool es_compra_grande(double monto, int cantidad) { return cantidad > 10 && monto > 1000; } bool es_compra_mediana(double monto, int cantidad) { return cantidad > 5 || monto > 500; } double descuento_miembro_compra_grande(int cantidad, const char* codigo_promo) { if (strcmp(codigo_promo, "VERANO") == 0) { return 0.30; } if (strcmp(codigo_promo, "FLASH") == 0) { return cantidad > 20 ? 0.35 : 0.25; } return 0.20; } double descuento_no_miembro_compra_grande(bool es_primera_compra) { return es_primera_compra ? 0.15 : 0.10; } double calcular_descuento(double monto, int cantidad, bool es_miembro, bool es_primera_compra, const char* codigo_promo) { if (es_compra_grande(monto, cantidad)) { if (es_miembro) { return descuento_miembro_compra_grande(cantidad, codigo_promo); } else { return descuento_no_miembro_compra_grande(es_primera_compra); } } if (es_compra_mediana(monto, cantidad)) { return es_miembro ? 0.15 : 0.05; } return es_primera_compra ? 0.10 : 0.0; }
Leyes de De Morgan¶
Útiles para simplificar expresiones booleanas complejas:
Leyes:
!(A && B)≡!A || !B!(A || B)≡!A && !B
Ejemplo:
1 2 3 4 5 6 7 8 9// Antes if (!(activo && validado)) { return; } // Después (más claro en contexto de guardia) if (!activo || !validado) { return; }
1 2 3 4 5 6 7 8 9// Antes if (!(edad < 18 || edad > 65)) { aplicar_tarifa_regular(); } // Después (más claro) if (edad >= 18 && edad <= 65) { aplicar_tarifa_regular(); }
Combinación con el Patrón de Único Retorno¶
Cuando se aplica el patrón de único retorno (ver Combinación con el Patrón de Único Retorno), las técnicas de simplificación de condicionales deben adaptarse para mantener un solo punto de salida.
Guardia de Cláusulas con Único Retorno¶
Guard Clauses tradicionales (múltiples returns):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17int procesar_pago(usuario_t* usuario, double monto) { if (usuario == NULL) { return ERROR_USUARIO_NULL; } if (!usuario->activo) { return ERROR_USUARIO_INACTIVO; } if (usuario->saldo < monto) { return ERROR_SALDO_INSUFICIENTE; } // Procesar pago usuario->saldo -= monto; return EXITO; }
Adaptado a único retorno:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17int procesar_pago(usuario_t* usuario, double monto) { int resultado = ERROR_DESCONOCIDO; if (usuario == NULL) { resultado = ERROR_USUARIO_NULL; } else if (!usuario->activo) { resultado = ERROR_USUARIO_INACTIVO; } else if (usuario->saldo < monto) { resultado = ERROR_SALDO_INSUFICIENTE; } else { // Procesar pago usuario->saldo -= monto; resultado = EXITO; } return resultado; }
Variables de Estado para Control de Flujo¶
Cuando se combina único retorno con condicionales complejos, usar variables de estado explícitas mejora la claridad.
Antes (múltiples returns):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19bool validar_transaccion(transaccion_t* trans) { if (trans->monto <= 0) { return false; } if (trans->origen == NULL || trans->destino == NULL) { return false; } if (trans->origen->saldo < trans->monto) { return false; } if (trans->origen->bloqueada || trans->destino->bloqueada) { return false; } return true; }
Después (único retorno con variable de estado):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15bool validar_transaccion(const transaccion_t* trans) { bool es_valida = true; if (trans->monto <= 0) { es_valida = false; } else if (trans->origen == NULL || trans->destino == NULL) { es_valida = false; } else if (trans->origen->saldo < trans->monto) { es_valida = false; } else if (trans->origen->bloqueada || trans->destino->bloqueada) { es_valida = false; } return es_valida; }
Combinando Predicados y Único Retorno¶
Con múltiples returns:
1 2 3 4 5 6bool puede_realizar_compra(const usuario_t* u, double monto) { if (!es_usuario_valido(u)) return false; if (!tiene_saldo_suficiente(u, monto)) return false; if (!esta_dentro_limite_diario(u, monto)) return false; return true; }
Con único retorno:
1 2 3 4 5 6 7 8 9 10 11bool puede_realizar_compra(const usuario_t* u, double monto) { bool puede = false; if (es_usuario_valido(u) && tiene_saldo_suficiente(u, monto) && esta_dentro_limite_diario(u, monto)) { puede = true; } return puede; }
O de manera más concisa:
bool puede_realizar_compra(const usuario_t* u, double monto) {
return es_usuario_valido(u) &&
tiene_saldo_suficiente(u, monto) &&
esta_dentro_limite_diario(u, monto);
}Caso Práctico: Autenticación con Único Retorno¶
Antes (múltiples returns):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23int autenticar(const char* usuario, const char* password) { if (usuario == NULL || password == NULL) { return AUTH_ERROR_PARAMETROS; } usuario_t* u = buscar_usuario(usuario); if (u == NULL) { return AUTH_ERROR_USUARIO_NO_EXISTE; } if (u->intentos_fallidos >= MAX_INTENTOS) { return AUTH_ERROR_BLOQUEADO; } if (!verificar_password(u, password)) { u->intentos_fallidos++; return AUTH_ERROR_PASSWORD_INCORRECTO; } u->intentos_fallidos = 0; u->ultimo_acceso = time(NULL); return AUTH_EXITO; }
Después (único retorno con flujo claro):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27int autenticar(const char* usuario, const char* password) { int resultado = AUTH_ERROR_DESCONOCIDO; usuario_t* u = NULL; // Validación de parámetros if (usuario == NULL || password == NULL) { resultado = AUTH_ERROR_PARAMETROS; } else { u = buscar_usuario(usuario); if (u == NULL) { resultado = AUTH_ERROR_USUARIO_NO_EXISTE; } else if (u->intentos_fallidos >= MAX_INTENTOS) { resultado = AUTH_ERROR_BLOQUEADO; } else if (!verificar_password(u, password)) { u->intentos_fallidos++; resultado = AUTH_ERROR_PASSWORD_INCORRECTO; } else { // Autenticación exitosa u->intentos_fallidos = 0; u->ultimo_acceso = time(NULL); resultado = AUTH_EXITO; } } return resultado; }
Manejo de Recursos con Único Retorno¶
El patrón de único retorno es especialmente útil cuando se manejan recursos que deben liberarse.
Antes (múltiples returns, riesgo de fugas):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31char* leer_archivo(const char* ruta) { FILE* f = fopen(ruta, "r"); if (f == NULL) { return NULL; } fseek(f, 0, SEEK_END); long tam = ftell(f); if (tam < 0) { fclose(f); // Fácil olvidar esto return NULL; } char* buffer = malloc(tam + 1); if (buffer == NULL) { fclose(f); // Y esto return NULL; } fseek(f, 0, SEEK_SET); size_t leidos = fread(buffer, 1, tam, f); if (leidos != tam) { free(buffer); // Y esto fclose(f); // Y esto return NULL; } buffer[tam] = '\0'; fclose(f); return buffer; }
Después (único retorno, liberación garantizada):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36char* leer_archivo(const char* ruta) { char* resultado = NULL; FILE* f = NULL; char* buffer = NULL; f = fopen(ruta, "r"); if (f != NULL) { fseek(f, 0, SEEK_END); long tam = ftell(f); if (tam >= 0) { buffer = malloc(tam + 1); if (buffer != NULL) { fseek(f, 0, SEEK_SET); size_t leidos = fread(buffer, 1, tam, f); if (leidos == tam) { buffer[tam] = '\0'; resultado = buffer; buffer = NULL; // No liberar si exitoso } } } } // Limpieza centralizada if (buffer != NULL) { free(buffer); } if (f != NULL) { fclose(f); } return resultado; }
Variables de Control para Lazos¶
El patrón de único retorno se combina bien con variables de control en lazos.
Antes (con break/return):
1 2 3 4 5 6 7 8int buscar_elemento(const int* arr, int n, int valor) { for (int i = 0; i < n; i++) { if (arr[i] == valor) { return i; // Retorno temprano } } return -1; }
Después (único retorno):
1 2 3 4 5 6 7 8 9 10 11int buscar_elemento(const int* arr, int n, int valor) { int indice = -1; for (int i = 0; i < n && indice == -1; i++) { if (arr[i] == valor) { indice = i; } } return indice; }
Balance entre Claridad y Único Retorno¶
Funciones simples - múltiples returns aceptables:
1 2 3 4 5 6 7bool es_par(int n) { return n % 2 == 0; } int maximo(int a, int b) { return (a > b) ? a : b; }
Funciones complejas - único retorno preferible:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28int procesar_pedido_complejo(pedido_t* pedido, contexto_t* ctx) { int resultado = ERROR_DESCONOCIDO; recurso_t* recurso = NULL; // Múltiples validaciones y manejo de recursos if (validar_pedido(pedido)) { recurso = adquirir_recurso(ctx); if (recurso != NULL) { if (procesar_con_recurso(pedido, recurso)) { resultado = EXITO; } else { resultado = ERROR_PROCESAMIENTO; } } else { resultado = ERROR_RECURSO; } } else { resultado = ERROR_VALIDACION; } // Limpieza garantizada if (recurso != NULL) { liberar_recurso(recurso); } return resultado; }
Resumen¶
Técnicas para simplificar condicionales:
Guard Clauses: Validaciones tempranas para reducir anidamiento
Variables Booleanas: Dar nombres descriptivos a condiciones complejas
Funciones de Predicado: Encapsular lógica condicional reutilizable
Simplificación Booleana: Retornar expresiones directamente
Polimorfismo: Reemplazar condicionales con despacho dinámico
Tablas de Decisión: Para lógica compleja con muchas combinaciones
Switch: En lugar de cadenas if-else para valores discretos
Único Retorno: Combinar con condicionales para mejor manejo de recursos
Principios clave:
Una condición debe ser inmediatamente comprensible
Preferir código plano sobre anidamiento profundo
Usar nombres descriptivos que documenten la intención
Extraer condiciones complejas a funciones
Aplicar Leyes de De Morgan para clarificar negaciones
El único retorno facilita manejo de recursos y depuración
Balance entre claridad y consistencia según el contexto
La claridad en los condicionales es esencial para código mantenible y libre de bugs.