IntermedioPatrones de diseño

Strategy

El patrón que convierte una cadena de if/else en piezas intercambiables — y la forma más directa de cumplir el principio abierto/cerrado.

En esta página

Strategy es un patrón de comportamiento: encapsula una familia de algoritmos intercambiables detrás de una interfaz común, de modo que el código que los usa (el contexto) no sabe — ni le importa — cuál está ejecutando. Es, probablemente, el patrón con mejor relación sencillez/impacto de todo el catálogo.

El problema: el switch que no deja de crecer

function calcularEnvio(pedido: Pedido, metodo: string): number {
  if (metodo === 'estandar') {
    return pedido.peso * 0.5;
  } else if (metodo === 'exprés') {
    return pedido.peso * 1.2 + 5;
  } else if (metodo === 'recogida') {
    return 0;
  }
  throw new Error(`método desconocido: ${metodo}`);
}

Hoy son tres ramas. Cada transportista nuevo modifica esta función — y de paso toca un fichero que ya funcionaba, con su riesgo de regresión. Si el mismo switch sobre metodo aparece además en la validación y en la UI, tienes el mismo conocimiento repetido en tres sitios: el problema exacto del que habla DRY.

La solución: algoritmos como valores

Cada rama se convierte en una estrategia con la misma firma, y el switch en un mapa:

type EstrategiaEnvio = (pedido: Pedido) => number;

const estrategias: Record<string, EstrategiaEnvio> = {
  estandar: (p) => p.peso * 0.5,
  expres: (p) => p.peso * 1.2 + 5,
  recogida: () => 0,
};

function calcularEnvio(pedido: Pedido, metodo: string): number {
  const estrategia = estrategias[metodo];
  if (!estrategia) throw new Error(`método desconocido: ${metodo}`);
  return estrategia(pedido);
}
ContextocalcularEnvio() Estrategia(pedido) → precio estándar exprés recogida

El contexto conoce la interfaz, nunca las implementaciones. Añadir una estrategia no toca al contexto: eso es el principio abierto/cerrado en acción.

Añadir un transportista ahora es añadir una entrada al mapa — el contexto queda intacto. El sistema está abierto a extensión, cerrado a modificación: acabas de ver el principio abierto/cerrado de SOLID funcionando, no en un eslogan.

En un lenguaje sin funciones de primera clase, cada estrategia sería una clase con un método (interface EstrategiaEnvio { calcular(p: Pedido): number }). En JavaScript/TypeScript, las funciones ya son objetos: el patrón se disuelve en el lenguaje, pero la idea — y el nombre para comunicarla — sigue siendo la misma.

Ya lo usas todos los días

  • arr.sort((a, b) => a.precio - b.precio) — el comparador es una estrategia que le inyectas al algoritmo de ordenación.
  • map, filter, reduce — cada callback es la estrategia de qué hacer con cada elemento.
  • Pasarelas de pago, proveedores de login (Google/GitHub/email), formatos de exportación (CSV/JSON/PDF): un contrato, N implementaciones.
  • La compresión de tu servidor HTTP (gzip/brotli según el cliente).

Strategy y sus vecinos

  • vs. Observer: Strategy elige cómo se hace una cosa (1 colaborador); Observer notifica que algo pasó (N interesados).
  • con Factory: ¿de dónde sale la estrategia correcta según la configuración? De una fábrica — los dos patrones suelen trabajar en pareja: la fábrica elige, la estrategia ejecuta.