¿Cómo puedo organizar mejor un código que se ocupa de la creación de muchos objetos basado en la concatenación de varios operadores ?: (ternary operator) evaluando un discriminante y creando uno u otro objeto según el discriminante?
Muy buena pregunta, Gabriel 👌. Cuando el código empieza a tener muchos operadores ternarios concatenados para decidir qué objeto crear, la legibilidad y el mantenimiento se vuelven un problema. Hay varias formas más limpias y escalables de organizarlo:
🔹 1. Usar un mapa de discriminantes a constructores
En vez de encadenar ternarios, podés definir un objeto/diccionario que mapea el discriminante a la función creadora:
type Discriminante = "A" | "B" | "C";
const factories: Record<Discriminante, () => Objeto> = {
A: () => new ObjetoA(),
B: () => new ObjetoB(),
C: () => new ObjetoC(),
};
function crearObjeto(tipo: Discriminante): Objeto {
return factories[tipo]();
}
👉 Ventaja: agregar un nuevo tipo es tan simple como añadir una entrada al mapa.
🔹 2. Usar un switch/case
Más claro que varios ternarios, especialmente si hay lógica adicional:
function crearObjeto(tipo: string): Objeto {
switch (tipo) {
case "A":
return new ObjetoA();
case "B":
return new ObjetoB();
case "C":
return new ObjetoC();
default:
throw new Error(`Tipo desconocido: ${tipo}`);
}
}
👉 Ventaja: legible, fácil de seguir, pero puede crecer mucho si hay muchos casos.
🔹 3. Patrón Factory
Si la creación de objetos es más compleja, podés encapsularla en una clase o módulo “Factory”:
class ObjetoFactory {
static crear(tipo: string): Objeto {
if (tipo === "A") return new ObjetoA();
if (tipo === "B") return new ObjetoB();
if (tipo === "C") return new ObjetoC();
throw new Error(`Tipo desconocido: ${tipo}`);
}
}
👉 Ventaja: centralizás la lógica de creación y podés extenderla con configuraciones, parámetros, etc.
🔹 4. Funciones puras con discriminante
Si los objetos comparten estructura, podés usar funciones que devuelvan configuraciones distintas:
function crearObjeto(tipo: string): Objeto {
const base = { activo: true };
if (tipo === "A") return { ...base, nombre: "Objeto A" };
if (tipo === "B") return { ...base, nombre: "Objeto B" };
return { ...base, nombre: "Default" };
}
👉 Ventaja: menos clases, más declarativo.
✨ Conclusión
Si son pocos casos →
switch/casees suficiente.Si son muchos y repetitivos → mejor un mapa de funciones.
Si la lógica es compleja → usá un Factory Pattern.
Esto evita el “spaghetti” de ternarios y hace que tu código sea más mantenible y escalable.
¿Querés que te arme un ejemplo con TypeScript + discriminated unions para que el compilador te ayude a asegurar que cada tipo de objeto está cubierto sin olvidos?
¡Perfecto, Gabriel! 👌 Ahora entiendo mejor tu caso: no estás creando instancias con new, sino que tu “creación de objetos” en realidad es renderizado de componentes JSX. En ese contexto, concatenar muchos operadores ternarios para decidir qué componente mostrar puede volverse ilegible. Te muestro formas más limpias de organizarlo:
🔹 1. Usar un mapa de discriminantes a componentes
En vez de encadenar ternarios, definís un diccionario que mapea el discriminante al componente JSX:
const componentMap = {
A: <ComponenteA />,
B: <ComponenteB />,
C: <ComponenteC />,
};
function Renderizador({ tipo }) {
return componentMap[tipo] ?? <ComponenteDefault />;
}
👉 Ventaja: agregar un nuevo caso es tan simple como añadir una entrada al mapa.
🔹 2. Usar un switch/case dentro de una función
Más claro que varios ternarios, especialmente si cada componente necesita props distintos:
function renderComponent(tipo) {
switch (tipo) {
case "A":
return <ComponenteA prop1="valor" />;
case "B":
return <ComponenteB />;
case "C":
return <ComponenteC />;
default:
return <ComponenteDefault />;
}
}
function App({ tipo }) {
return <div>{renderComponent(tipo)}</div>;
}
🔹 3. Patrón Factory declarativo
Podés encapsular la lógica en una función “factory” que devuelva JSX:
function ComponentFactory({ tipo }) {
if (tipo === "A") return <ComponenteA />;
if (tipo === "B") return <ComponenteB />;
if (tipo === "C") return <ComponenteC />;
return <ComponenteDefault />;
}
👉 Esto centraliza la lógica y mantiene tu JSX principal limpio.
🔹 4. Discriminated unions con TypeScript
Si usás TypeScript, podés aprovechar los union types para que el compilador te obligue a cubrir todos los casos:
type Tipo = "A" | "B" | "C";
function ComponentFactory({ tipo }: { tipo: Tipo }) {
switch (tipo) {
case "A": return <ComponenteA />;
case "B": return <ComponenteB />;
case "C": return <ComponenteC />;
}
}
👉 Si olvidás un caso, TypeScript te avisa.
✨ Conclusión
Muchos ternarios → ilegible.
Mejor usar un mapa de componentes o un switch/case.
Si querés escalabilidad, encapsulá la lógica en un factory declarativo.
Con TypeScript, los union types te ayudan a no olvidar casos.
¿Querés que te arme un ejemplo completo con mapa de componentes + props dinámicos (ej. cada componente recibe props distintos según el discriminante) para que veas cómo se organiza en un proyecto real?
No hay comentarios:
Publicar un comentario