Cómo mejorar los condicionales con objetos y funciones
Antes de empezar
Los condicionales son fundamentales en programación, pero a menudo generan código difícil de mantener. Te voy a mostrar cómo transformar largas cadenas de if/else y switch en soluciones más elegantes usando objetos y funciones.
Problemáticas de los condicionales tradicionales
1. Cadenas largas de if/else
function obtenerSaludo(idioma) {
if (idioma === 'es') {
return '¡Hola!';
} else if (idioma === 'en') {
return 'Hello!';
} else if (idioma === 'fr') {
return 'Salut!';
} else if (idioma === 'it') {
return 'Ciao!';
} else if (idioma === 'de') {
return 'Hallo!';
} else {
return 'Hi!';
}
}
Problemas:
- Código repetitivo y poco verboso
- Difícil de mantener cuando crece
- Nos saltamos el principio Open/Closed
2. Switch statements largos
function calcularDescuento(tipoCliente) {
switch (tipoCliente) {
case 'premium':
return 0.20;
case 'gold':
return 0.15;
case 'silver':
return 0.10;
case 'bronze':
return 0.05;
default:
return 0;
}
}
Problemas:
- Fácil olvidar el
break - No escalable
- Dificultad para testing individual
Solución 1: Objetos como mapas
Ejemplo básico: Saludos
const saludos = {
es: '¡Hola!',
en: 'Hello!',
fr: 'Salut!',
it: 'Ciao!',
de: 'Hallo!'
};
function obtenerSaludo(idioma) {
return saludos[idioma] || 'Hi!';
}
// Uso
console.log(obtenerSaludo('es')); // ¡Hola!
Ejemplo avanzado: Piedra, Papel o Tijera
const gameRules = {
piedra: 'tijera', // piedra gana a tijera
papel: 'piedra', // papel gana a piedra
tijera: 'papel' // tijera gana a papel
};
function determinarGanador(jugador, maquina) {
if (jugador === maquina) return 'Empate';
return gameRules[jugador] === maquina ? 'Ganaste' : 'Perdiste';
}
// Uso
console.log(determinarGanador('piedra', 'tijera')); // Ganaste
console.log(determinarGanador('papel', 'piedra')); // Ganaste
Solución 2: Objetos con funciones
Calculadora con operaciones
const operaciones = {
suma: (a, b) => a + b,
resta: (a, b) => a - b,
multiplicacion: (a, b) => a * b,
division: (a, b) => b !== 0 ? a / b : 'Error: División por cero'
};
function calcular(operacion, a, b) {
const operador = operaciones[operacion];
return operador ? operador(a, b) : 'Operación no válida';
}
// Uso
console.log(calcular('suma', 5, 3)); // 8
console.log(calcular('division', 10, 2)); // 5
Sistema de validaciones
const validadores = {
email: (valor) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(valor),
telefono: (valor) => /^\d{9}$/.test(valor),
password: (valor) => valor.length >= 8,
nombre: (valor) => valor.length >= 2 && /^[a-zA-ZáéíóúÑñ\s]+$/.test(valor)
};
function validarCampo(tipo, valor) {
const validador = validadores[tipo];
return validador ? validador(valor) : false;
}
// Uso
console.log(validarCampo('email', 'test@email.com')); // true
console.log(validarCampo('telefono', '123456789')); // true
Solución 3: Map para casos complejos
Gestión de estados
const estadosUsuario = new Map([
['activo', { color: 'green', mensaje: 'Usuario activo', acciones: ['editar', 'eliminar'] }],
['inactivo', { color: 'gray', mensaje: 'Usuario inactivo', acciones: ['activar'] }],
['bloqueado', { color: 'red', mensaje: 'Usuario bloqueado', acciones: ['desbloquear'] }],
['pendiente', { color: 'yellow', mensaje: 'Pendiente de verificación', acciones: ['verificar'] }]
]);
function obtenerInfoUsuario(estado) {
return estadosUsuario.get(estado) || {
color: 'black',
mensaje: 'Estado desconocido',
acciones: []
};
}
// Uso
const info = obtenerInfoUsuario('activo');
console.log(info.mensaje); // Usuario activo
console.log(info.acciones); // ['editar', 'eliminar']
Comparación de rendimiento
Método tradicional (lento)
function obtenerPrecio(producto) {
if (producto === 'laptop') return 1200;
if (producto === 'mouse') return 25;
if (producto === 'teclado') return 80;
if (producto === 'monitor') return 300;
if (producto === 'altavoces') return 150;
// ... más productos
return 0;
}
Método optimizado (rápido)
const precios = {
laptop: 1200,
mouse: 25,
teclado: 80,
monitor: 300,
altavoces: 150
};
const obtenerPrecio = (producto) => precios[producto] || 0;
Ternarios: cuándo usarlos
Casos apropiados
// ✅ Bueno: asignación simple
const mensaje = usuario.activo ? 'Bienvenido' : 'Cuenta desactivada';
// ✅ Bueno: clases CSS
const className = `btn ${esImportante ? 'btn-primary' : 'btn-secondary'}`;
Casos a evitar
// ❌ Malo: ternarios anidados
const resultado = condicion1 ?
(condicion2 ? 'caso1' : 'caso2') :
(condicion3 ? 'caso3' : 'caso4');
// ✅ Mejor: objeto
const casos = {
[condicion1 && condicion2]: 'caso1',
[condicion1 && !condicion2]: 'caso2',
[!condicion1 && condicion3]: 'caso3'
};
const resultado = Object.entries(casos).find(([key]) => key)?.[1] || 'caso4';
Sistema de notificaciones
const tiposNotificacion = {
success: {
icono: '✅',
color: '#4CAF50',
duracion: 3000,
sound: 'success.mp3'
},
error: {
icono: '❌',
color: '#F44336',
duracion: 5000,
sound: 'error.mp3'
},
warning: {
icono: '⚠️',
color: '#FF9800',
duracion: 4000,
sound: 'warning.mp3'
},
info: {
icono: 'ℹ️',
color: '#2196F3',
duracion: 3000,
sound: 'info.mp3'
}
};
function mostrarNotificacion(tipo, mensaje) {
const config = tiposNotificacion[tipo];
if (!config) {
console.error('Tipo de notificación no válido');
return;
}
// Crear y mostrar notificación
const notificacion = {
mensaje,
...config,
timestamp: Date.now()
};
console.log(`${config.icono} ${mensaje}`);
// Aquí iría la lógica de UI real
return notificación;
}
Beneficios de estas técnicas
🚀 Rendimiento: Acceso O(1) vs O(n) en if/else largos
🧹 Mantenibilidad: Código más limpio y fácil de modificar
📈 Escalabilidad: Agregar nuevos casos es trivial
🧪 Testabilidad: Cada función puede testearse independientemente
🔒 Inmutabilidad: Los objetos pueden ser congelados para evitar modificaciones
const configInmutable = Object.freeze({
development: { debug: true, apiUrl: 'localhost:3000' },
production: { debug: false, apiUrl: 'api.miapp.com' }
});
Estas técnicas no solo mejoran la legibilidad del código, sino que también lo hacen más eficiente y mantenible. La próxima vez que veas una larga cadena de if/else, recuerda que probablemente hay una forma más elegante de resolverlo.