Curso exprés · No. 04

La mayoría ya los reinventaste a mano. Un patrón es solo el nombre — para que un equipo pueda decir en una palabra lo que llevaría un párrafo, y reconocer la jugada antes de hacerla mal.

Solo la esencia · Una imagen por patrón · Ejemplos antes que UML

§ 01

Un patrón de diseño es una solución con nombre y reutilizable a un problema que aparece una y otra vez en el código. Es vocabulario, no una librería — no lo instalás, lo reconocés.

Un patrón es el nombre de una jugada que ya hacés

Los futbolistas no inventan «la pared» en medio del partido. Tiene un nombre, y decir el nombre es más rápido que describir toda la jugada.

La mayoría de los patrones son cosas que los desarrolladores competentes reinventan a mano de todos modos. Ponerles nombre convierte un párrafo de explicación en una palabra que todo el equipo comparte. Cuando alguien dice «envolvelo en un Decorator», todos ven la forma al instante — y ese es todo el sentido de un patrón: un vocabulario compartido para jugadas que harías igual.

Los patrones se descubren, no se inventan

Nadie inventó el arco. Los constructores seguían encontrando que era la forma más fuerte de salvar un vano — así que le pusieron un nombre.

La Banda de los Cuatro no diseñó 23 trucos ingeniosos; catalogó soluciones que seguían reapareciendo en el buen código. Así que un patrón no es un objetivo que cumplir — es una forma a la que llegás naturalmente cuando el problema lo pide. Si estás forzando uno, probablemente todavía no tenés el problema.

Tres familias, tres preguntas

Una cocina tiene herramientas para tres tareas: preparar la comida, emplatarla y servirla. Los patrones se dividen igual.

Los patrones de creación son sobre cómo nacen los objetos — quién los construye, y cómo. Los patrones estructurales son sobre cómo encajan los objetos en formas más grandes. Los patrones de comportamiento son sobre cómo hablan los objetos y reparten responsabilidad. Casi todo patrón clásico es una de esas tres respuestas.

Un patrón que no necesitás es solo complejidad

Una navaja suiza en el bolsillo es práctica. Atornillar las veinte herramientas a la puerta de tu casa, no.

Todo patrón agrega indirección — una capa entre vos y la cosa. Vale la pena cuando te compra flexibilidad que realmente vas a usar, y es puro costo cuando no. El error común no es ignorar los patrones; es echar mano de uno antes de que el problema exista.

Un patrón es un atajo para una conversación. Usalo para que te entiendan, no para parecer ingenioso.

§ 02

Cómo cobran vida los objetos — sin desparramar new por todas partes y sin soldar tu código a clases concretas.

Factory: pedí una cosa por su nombre, no por su receta

Una máquina de café: apretás «latte» y te sale uno. No molés, espumás ni servís vos — y no necesitás saber cómo se hace.

Una factory te entrega un objeto sin que llames a su constructor directamente, así el código que llama nunca depende de la clase concreta. Ejemplo: createPaymentProvider("stripe") devuelve algo que satisface una interfaz PaymentProvider — cambiás a PayPal modificando la factory, no cada lugar que la llama. Te compra la libertad de cambiar qué se construye sin tocar a quien lo pidió.

Abstract Factory: toda una familia coherente de una vez

Comprar un juego de muebles — «Victoriano» o «Moderno» — donde cada pieza llega en el mismo estilo, sin sillas que desentonen.

Cuando los objetos deben venir en familias coherentes, una abstract factory construye el conjunto entero para que encajen. Ejemplo: un toolkit de UI que produce botones, menús y diálogos a juego para macOS frente a Windows desde una sola factory. Poderosa — pero es maquinaria pesada, vale la pena solo cuando las familias son reales.

Builder: armá un objeto complejo paso a paso

Pedir una hamburguesa a medida — pan, carne, sin cebolla, queso extra — una elección a la vez, y después «listo».

Cuando un objeto tiene muchas partes opcionales, un builder te deja fijarlas pieza por pieza en lugar de un constructor monstruoso con doce argumentos. Ejemplo: QueryBuilder().select("name").where("age > 18").limit(10).build(). Cambia un constructor desconcertante por una cadena legible.

Singleton: exactamente uno, compartido por todos

Un país tiene un solo presidente. Todo el que se refiere a «el presidente» habla de la misma persona — no hay dos.

Un singleton garantiza una única instancia con un punto de acceso global. Ejemplo: un objeto de configuración o un logger del que todos leen. Pero manejalo con cuidado: un singleton es estado global con un saco lindo, y el estado global hace difícil testear y razonar. A menudo una única instancia que pasás explícitamente le gana a una alcanzada a través de un global.

Prototype: copiá una cosa ya completada en vez de construir de cero

Fotocopiar un formulario completado es más rápido que llenar uno en blanco desde cero.

Cuando hacer un objeto de cero es caro o engorroso, cloná uno existente y ajustalo. Ejemplo: duplicar un enemigo de juego ya configurado, o un nodo de documento con estilos, en vez de rehacer su setup cada vez. Práctico cuando los objetos son costosos de construir y mayormente parecidos.

Escondé el new. Cuantos menos lugares sepan cómo se construye un objeto, más libre sos de cambiarlo.

§ 03

Cómo se componen los objetos en estructuras más grandes — encajando partes incompatibles y agregando capacidad sin reescribir.

Adapter: un conversor de enchufe entre dos interfaces

El cargador de tu laptop en el extranjero — el dispositivo está bien, el tomacorriente está bien; solo necesitás el conversorcito entre ambos.

Un adapter envuelve una interfaz para que parezca otra, dejando que dos cosas que no fueron diseñadas para cooperar trabajen juntas. Ejemplo: envolver un SDK de pagos de terceros para que satisfaga tu propia interfaz PaymentProvider — tu código nunca ve la forma del proveedor. Es como evitás que la API de otro se filtre por todo tu codebase.

Decorator: agregá toppings sin cambiar la pizza

Una pizza simple, después queso, después champiñones — cada capa envuelve a la anterior y agrega algo, y sigue siendo una pizza.

Un decorator envuelve un objeto para agregar comportamiento sin modificar el original ni hacer subclases. Ejemplo: envolver un stream de datos para agregar compresión, después encriptado — cada uno una capa que podés apilar o quitar. Te deja agregar features en combinaciones que nadie tuvo que anticipar.

Facade: una sola recepción simple sobre una oficina desordenada

Un conserje de hotel — decís «necesito un taxi y una reserva para cenar», y él se ocupa de la maraña de llamadas detrás del mostrador.

Una facade da un punto de entrada simple y único a un subsistema complicado, escondiendo sus partes móviles. Ejemplo: un VideoConverter.convert(file, "mp4") que orquesta calladito codecs, buffers y streams de audio por debajo. Achica una gran superficie hasta la única puerta que la mayoría de los que llaman necesitan.

Proxy: un suplente que controla el acceso

El asistente de una celebridad — no llegás a la estrella directamente; el asistente filtra, agenda y a veces responde por ella.

Un proxy se pone delante de un objeto para controlar el acceso a él — para lazy loading, permisos, caching o hablar con algo remoto. Ejemplo: un proxy de imagen que no carga el archivo real hasta que se muestra; o un proxy de acceso que chequea permisos antes de reenviar la llamada. La misma interfaz, una compuerta extra delante.

Composite: tratá igual a un árbol y a una hoja

Una carpeta contiene archivos y otras carpetas. «Calcular el tamaño» funciona igual le preguntes a un archivo o al árbol entero.

Un composite te deja tratar de manera uniforme a objetos individuales y a grupos de objetos, así una rama y una hoja comparten una sola interfaz. Ejemplo: una UI donde un botón y un panel-lleno-de-botones responden ambos render() y getSize(). Convierte las estructuras recursivas en algo que manejás sin tratar como caso especial cada nivel.

Composición antes que herencia: ensamblá el comportamiento en runtime en vez de congelarlo en un árbol de clases.

§ 04

Cómo reparten trabajo los objetos y cómo hablan entre sí — para que la responsabilidad se divida limpio y el código correcto corra en el momento correcto.

Strategy: cambiá el algoritmo, mantené al que llama

Una app de mapas: el mismo viaje, pero elegís «más rápido», «más corto» o «evitar peajes», y te rutea distinto. Vos elegís la estrategia; la app solo la sigue.

Strategy hace un algoritmo intercambiable en runtime, así el que llama queda igual mientras el método varía. Ejemplo: un checkout que toma una PricingStrategy — regular, de socio o de liquidación — en vez de una escalera de if que crece. En los lenguajes modernos esto suele ser solo pasar una función en lugar de toda una clase — que es justo el punto: el patrón es la idea, no la ceremonia.

Observer: suscribite, y que te avisen cuando cambie

Un newsletter — te suscribís una vez, y cada número nuevo te llega. El editor no llama por teléfono a cada lector; solo manda a la lista.

Observer deja que los objetos se suscriban a otro y reciban aviso cuando cambia, sin que este sepa quiénes son. Ejemplo: una celda de planilla que refresca cada gráfico que depende de ella; frameworks de UI reaccionando al estado; pub/sub por todas partes. Es la columna vertebral del código orientado a eventos y reactivo.

Command: envolvé una request como un objeto

El ticket de pedido de un restaurante — la request «mesa 5, dos cafés» se vuelve un papel que podés encolar, reordenar, registrar e incluso romper.

Command convierte una acción en un objeto independiente, así podés encolarla, registrarla, agendarla o deshacerla. Ejemplo: cada edición en un editor es un comando — que es exactamente lo que hace posibles el undo/redo y las macros; las colas de jobs son comandos esperando su turno. Es cómo una acción se vuelve algo que podés guardar y reproducir.

Chain of Responsibility: pasalo hasta que alguien lo maneje

Los niveles de soporte al cliente — la recepción intenta, escala a un especialista, después a un gerente, hasta que alguien puede resolverlo de verdad.

Una request viaja por una cadena de handlers; cada uno la maneja o la pasa. Ejemplo: middleware web — auth, después logging, después rate-limiting, después la ruta — cada eslabón manejando lo suyo o reenviando. Desacopla al emisor de quien termina haciendo el trabajo.

Los buenos patrones de comportamiento dejan que dos piezas cooperen sin que ninguna cablee a la otra por dentro.

§ 05

Cómo cambia el comportamiento de un objeto con su estado, cómo recorrés colecciones, y cómo fijás un esqueleto dejando un paso abierto.

State: comportamiento que cambia con el modo en que estás

Un semáforo — verde, amarillo, rojo — donde cada color decide qué pasa ahora y qué color viene después. El mismo semáforo, distinto comportamiento por estado.

El patrón State deja que un objeto cambie su comportamiento a medida que cambia su estado interno, reemplazando una maraña de if y switch por estados y transiciones claros. Ejemplo: un pedido que se comporta distinto según esté pending, paid, shipped o cancelled, cada estado conociendo solo sus propias reglas y en qué puede convertirse. Convierte un lío de flags en una maquinita prolija.

Iterator: recorré una colección sin ver sus tripas

El siguiente y anterior de un control remoto — te movés por los canales sin saber cómo están guardados.

Iterator da una forma uniforme de recorrer una colección sin exponer cómo está construida — array, árbol o lista enlazada. Ejemplo: for (item of collection) funciona igual sobre una lista o una estructura a medida. Este es tan útil que el lenguaje normalmente lo trae incorporadofor..of, generadores, __iter__ — la señal más clara de que un patrón puede graduarse en feature.

Template Method: una receta fija con un blanco para completar

Una receta idéntica cada vez — salvo «agregá la especia que prefieras» en el paso cuatro. El esqueleto es fijo; un paso es tuyo.

Template Method define el esqueleto de una operación y deja que los pasos específicos se sobreescriban sin cambiar la forma general. Ejemplo: un importador de datos base que abre, lee, parsea (esto lo completás vos) y guarda — donde los importadores de CSV y JSON aportan solo el paso de parseo. Útil cuando muchos flujos comparten una forma pero difieren en uno o dos puntos.

Y el resto, de un saque

Una caja de herramientas tiene herramientas que agarrás mes a mes, no a diario — pero igual deberías saber que existen.

Un puñado más se gana su lugar de vez en cuando: Mediator (una torre de control para que los objetos hablen a través de un solo hub en lugar de una maraña de enlaces directos), Visitor (agregar operaciones nuevas a una estructura sin editar sus clases), Memento (capturar el estado de un objeto para restaurarlo después — save points, snapshots de undo). Conocé los nombres; echá mano de ellos solo cuando aparezca el problema exacto.

Cuando un patrón se vuelve una palabra clave del lenguaje, dejá de escribir el patrón y usá la palabra clave.

§ 06

El catálogo son décadas de sabiduría ganada a los golpes — y también un producto de su época. Usalo como un ingeniero senior, no como un checklist.

Muchos patrones ya son solo features del lenguaje

Antes dábamos indicaciones por puntos de referencia; ahora el teléfono navega. La habilidad no desapareció — quedó absorbida en la herramienta.

Iterator es for..of. Strategy es a menudo una función que pasás. Decorator y Observer son ciudadanos de primera clase en los frameworks modernos. Cuando el lenguaje te entrega la forma gratis, escribir el patrón entero a mano es pura ceremonia. La idea sigue importando; el boilerplate normalmente no.

La patrón-itis es una enfermedad real

Un cocinero que vuelca todo el especiero en un solo plato no consigue una comida más rica — consigue una incomible.

El clásico olor a sobre-ingeniería es echar mano de patrones para parecer sofisticado: una abstract factory para un solo producto, un Strategy para un solo algoritmo que nunca varía, cinco capas de indirección alrededor de un if. Y ahora es peor — un asistente de IA te va a generar feliz una "Multi-Level Abstract Factory" para lo que deberían ser tres líneas. El código más simple es casi siempre la elección más senior.

Los patrones son un vocabulario, no una ley

Saber muchas palabras no te hace un buen escritor. Saber qué palabra — y cuándo callarte — sí.

El valor real del catálogo es el lenguaje compartido: «pongamos una Facade acá» aterriza al instante con cualquiera que conozca el término, y eso vale mucho en un equipo. Pero el objetivo es siempre el código más simple que resuelve el problema. El patrón es un medio; nombrar uno nunca es el logro.

Aprendé todos los patrones para poder reconocerlos. Usá los menos que puedas.

§ 07

Los patrones son respuestas. La habilidad está en emparejarlos con una pregunta real — y en saber cuándo ningún patrón es la respuesta correcta.

Dejá que el patrón emerja del dolor

No elegís la llave antes de encontrar el perno. Ves el problema, y después echás mano de la herramienta que le encaja.

El buen uso de patrones suele ser un refactor, no un plano de partida: escribís lo simple, sentís un dolor específico — esta escalera de if no para de crecer, esta clase sabe demasiado sobre cómo se construye — y entonces el patrón que lo alivia es obvio. Forzar patrones de entrada es como construís catedrales que nadie pidió.

Nombralo solo cuando el nombre ayuda

No decís «ejecuté una pared» en una charla casual. Pero en la cancha, con compañeros, la palabra ahorra una frase entera.

El rédito de un patrón es comunicación y flexibilidad. Si nombrarlo le aclara el código a la próxima persona, usá el nombre en voz alta — en el nombre de una clase, un comentario, una charla de diseño. Si la indirección no compra nada, gana el código más llano, haya patrón o no.

Antes de echar mano de un patrón
  • ¿Qué problema recurrente resuelve? Decilo en una frase. - ¿Tengo ese problema ahora, o estoy adivinando sobre el futuro? - ¿Una función u objeto plano alcanzaría? Probá eso primero.
  • ¿Mi lenguaje ya me da esto gratis? - ¿Nombrarlo le aclarará el código a la próxima persona — o solo lo hará más vistoso? - ¿Estoy refactorizando hacia él, o forzándolo de entrada?
Tests de olor de que sobre-ingenierizaste
  • Tenés una factory que construye exactamente una sola cosa. - Un Strategy con una sola estrategia que nunca cambia. - Más interfaces e indirección que comportamiento real. - Agregaste el patrón para parecer senior, no para resolver un dolor. - Un lector nuevo necesita un diagrama UML para seguir tres líneas de lógica.
Señales de que elegiste bien
  • El patrón eliminó una escalera de if/switch que no paraba de crecer. - Podés cambiar una implementación sin tocar a los que llaman. - Un compañero reconoció la forma por su nombre al instante. - La indirección se gana su lugar — realmente usás la flexibilidad. - El código es más simple con el patrón que sin él, no solo más ingenioso.

El mejor uso de un patrón de diseño es el que un lector nunca nota — porque hizo que el código pareciera obvio.

Fin del curso exprés · 7 capítulos · patrones antes que UML

Lo que sigue es la fuente: Design Patterns de la Banda de los Cuatro, y sus compañeros modernos más amables — Head First Design Patterns, refactoring.guru, Refactoring de Martin Fowler. Pero leelos como aprenderías aperturas de ajedrez: no para memorizar jugadas, sino para reconocer la posición cuando aparezca en tu propio tablero.