Módulo 1: Qué son de verdad los patrones
2. Un patrón es una solución con nombre a un problema que se repite
Descripción
Al terminar esta lección vas a tener una definición de "patrón de diseño" que puedes decir en una frase y defender en una discusión, y —más útil— vas a tener una plantilla mental para leer cualquier patrón, incluidos los que esta guía no cubre y los que todavía no existen. Esa plantilla tiene tres casillas: el problema que el patrón resuelve, la solución estructural que propone, y las consecuencias de aplicarla, o sea lo que ganas y lo que pagas. Al final de la lección vas a poder tomar cualquier ficha de patrón —del libro original, de una página web, de un video— y llenar esas tres casillas por tu cuenta.
Esto importa porque de las tres casillas, la mayoría de la gente estudia una y media. El problema se lee por encima; la solución se estudia con detalle, porque es la que tiene el diagrama bonito y el código de ejemplo; y las consecuencias se saltan casi siempre. El resultado es previsible: alguien sabe cómo se construye un Decorator pero no sabe cuándo le conviene, ni qué se rompe cuando lo mete. Y como la tercera casilla es la única que contiene los costos, saltársela produce exactamente el perfil de programador que introduce patrones donde estorban, con toda la buena fe del mundo.
Hay algo más, y quiero decirlo temprano: la tercera casilla es donde vive el criterio. Toda esta guía promete dos cosas —vocabulario y criterio—. El vocabulario sale de la primera y la segunda casilla. El criterio sale entero de la tercera. Cuando en el módulo 8 tengas que defender un refactor frente a tu equipo, lo que vas a decir no es "porque es una Strategy": vas a decir "porque ganamos esto y pagamos aquello, y en este caso vale la pena". Eso es la tercera casilla hablando.
Conexión con el módulo: la lección 1 dejó la idea de que un patrón es un nombre. Esta lección la vuelve precisa: un nombre, sí, pero un nombre para una tripleta problema-solución-consecuencias. La lección 3 toma la parte del nombre y muestra qué hace en una conversación de equipo. La lección 4 explica de dónde salieron estas tripletas y por qué se descubrieron en vez de inventarse. La lección 5 usa la casilla del problema como técnica de reconocimiento: para identificar un patrón en código ajeno se busca el problema, no la forma. Y todo el módulo 2 —la guía entera casi— es un desarrollo de la tercera casilla.
Los nudos de un marinero
Sube a un velero con alguien que sepa navegar y vas a escuchar palabras raras: as de guía, ballestrinque, ocho, vuelta de escota. Cada una nombra un nudo. Y si le pides que te explique uno, no te va a describir solamente cómo se hace. Te va a decir tres cosas, siempre en el mismo orden.
Primero, para qué sirve. "El as de guía es para hacer un lazo en la punta de un cabo, un lazo que no se aprieta por más que tires." Fíjate que eso no es una descripción del nudo: es una descripción del problema. Necesitas un lazo. Necesitas que el lazo mantenga su tamaño aunque le cuelgues una carga. Ese problema aparece constantemente en un barco, y por eso el nudo tiene nombre.
Segundo, cómo se hace. Ahí sí viene la secuencia: el chicote sale, da la vuelta, entra por el ojo, rodea el firme, vuelve a entrar. Es la parte que se puede dibujar. Es la parte que aprendes con las manos.
Y tercero —esta es la que separa al que navega del que se aprendió el nudo en un video—, qué pasa cuando lo usas. "El as de guía es fácil de deshacer incluso después de haber aguantado mucha carga; por eso es el que quieres cuando vas a tener que soltarlo. Pero es de los que se afloja si el cabo queda flojo y se sacude, así que no lo dejes solo bajo cargas cíclicas. Y con cabo sintético moderno, muy liso, conviene rematarlo."
Esa tercera parte no está en el dibujo. No se ve en la foto del nudo terminado. Se aprende de alguien que ya perdió algo por confiar en el nudo equivocado. Y es exactamente la parte que decide cuál de los nudos usar, porque hay varios que hacen un lazo, y lo que los distingue no es la forma: son las consecuencias.
Un patrón de diseño es esto mismo. Un nombre, un problema recurrente, una forma de resolverlo, y un conjunto de consecuencias que casi nadie te cuenta. Nuestra definición de trabajo, entonces:
Un patrón de diseño es una solución con nombre a un problema de estructura que se repite, junto con las consecuencias de aplicarla.
Vale la pena desmenuzar cada pieza de esa frase, porque cada una está ahí por algo.
"Solución" — no es una regla, ni un principio, ni una obligación. Es una manera de resolver algo. Puede haber otras, y a veces la otra es mejor.
"Con nombre" — el nombre es la mitad del valor. Lo vimos en la lección 1 y la lección 3 lo desarrolla. Sin nombre, la solución existe pero no se puede conversar.
"Un problema de estructura" — patrones de diseño, no de algoritmos. No te dicen cómo ordenar una lista rápido; te dicen cómo acomodar las piezas de tu programa para que cierto tipo de cambio no duela. El problema siempre tiene esta forma: "algo va a variar, y no quiero que esa variación se me riegue por todo el código".
"Que se repite" — esta es la palabra que más gente se salta. Si el problema aparece una sola vez en tu vida, no necesita un patrón: necesita una solución. Los patrones existen porque ciertos problemas aparecen una y otra vez, en proyectos distintos, en empresas distintas, en lenguajes distintos. La lección 4 vuelve sobre esto.
"Junto con las consecuencias" — sin esta parte, un patrón es un truco. Con ella, es una decisión de ingeniería.
Ejemplo trabajado: las tres casillas sobre un rincón real de Boletia
Vamos a llenar las tres casillas sobre código de verdad. Abre pricing/ de Boletia. Hoy, calcular el precio de un boleto se ve así:
# Archivo: pricing/calculator.py
# Estado actual: un condicional que crece cada vez que marketing inventa algo.
def calculate_price(ticket, order_date):
# El precio depende del tipo de boleto. Hoy hay cuatro tipos.
if ticket.kind == "general":
return ticket.base_price
elif ticket.kind == "vip":
# Los VIP pagan 40% más que el precio base del evento.
return ticket.base_price * 1.40
elif ticket.kind == "early_bird":
# Descuento del 25% si la compra ocurre antes de la fecha de corte.
# Si ya pasó la fecha, el early-bird simplemente cuesta lo normal.
cutoff = get_early_bird_cutoff(ticket.event_id)
if order_date < cutoff:
return ticket.base_price * 0.75
return ticket.base_price
elif ticket.kind == "courtesy":
# Las cortesías no se cobran, pero hay un tope por evento.
# Si ya se pasó el tope, esto no debería haberse emitido: fallamos ruidosamente.
if courtesy_count(ticket.event_id) > COURTESY_LIMIT:
raise CourtesyLimitExceeded(ticket.event_id)
return 0.0
else:
raise ValueError(f"Tipo de boleto desconocido: {ticket.kind}")
Es código honesto. Funciona. Es fácil de leer de arriba a abajo. Y aun así, la mayoría de la gente que lo ve dice "aquí hay una Strategy". Vamos a ver por qué, llenando las tres casillas —y ojo, todavía no vamos a implementar nada; eso es el módulo 3—.
Casilla 1: el problema. Formulado sin nombrar ningún patrón, el problema es este: tenemos una operación —calcular el precio— cuyo comportamiento depende de un tipo, y ese tipo va a seguir creciendo. Marketing ya pidió boletos de "preventa para socios" y de "paquete familiar". Cada tipo nuevo obliga a abrir esta función, entender las cuatro ramas que ya están, y meter una quinta sin romperlas. Y ese elif no vive solo: hay otras funciones en Boletia que también preguntan por ticket.kind —una que decide si el boleto es transferible, otra que decide si aplica devolución—. Es decir, la misma pregunta está contestada en varios lugares, y agregar un tipo obliga a acordarse de todos.
Fíjate cómo está descrito el problema: sin la palabra Strategy, sin hablar de clases ni de interfaces. Solo la incomodidad concreta. Esa es la forma correcta de llenar la casilla 1, y es una habilidad en sí misma. Si no puedes describir el problema sin nombrar el patrón, todavía no entendiste el problema.
Casilla 2: la solución estructural. El patrón Strategy propone lo siguiente: sacar cada comportamiento variable a su propio objeto, hacer que todos esos objetos tengan la misma forma —el mismo método, con los mismos parámetros—, y que el código que necesita el comportamiento reciba uno de esos objetos y lo use sin saber cuál le tocó.
Traducido a Boletia: una PricingRule con un método price_for(ticket, order_date), cuatro implementaciones (GeneralPricing, VipPricing, EarlyBirdPricing, CourtesyPricing), y un calculate_price que ya no pregunta por el tipo, sino que recibe la regla que le toca y la aplica. Agregar un quinto tipo pasa a ser crear un archivo nuevo en vez de editar el archivo de todos.
Nota que la solución no elimina la decisión: alguien, en algún lugar, tiene que decidir qué regla corresponde a qué boleto. Lo que hace la Strategy es mover esa decisión a un solo punto en vez de tenerla repartida. Esa distinción —mover, no eliminar— es de las cosas que menos se dicen y más importan. Volveremos a ella.
Casilla 3: las consecuencias. Y aquí viene lo que casi ninguna explicación de Strategy te cuenta con honestidad.
Lo que ganas: agregar un tipo de precio deja de tocar código existente, lo que reduce el riesgo de romper lo que ya funciona. Cada regla queda aislada y se puede probar sola, con un test pequeño que no necesita montar una orden completa. Las reglas se pueden combinar o intercambiar en tiempo de ejecución —útil si algún día un evento quiere sus propias reglas—. Y la pregunta "¿cuál es la regla del VIP?" tiene una respuesta de un archivo en vez de una rama dentro de una función larga.
Lo que pagas: pasas de un archivo a seis. Para entender cómo se calcula un precio ya no lees una función de arriba a abajo: abres el calculador, ves que recibe una regla, buscas quién decide la regla, y de ahí saltas al archivo de la regla concreta. Eso son tres saltos donde antes había cero. Un desarrollador nuevo tarda más en ubicarse. La comparación de las cuatro reglas entre sí —"¿el VIP suma más que lo que descuenta el early-bird?"— que antes era leer treinta líneas seguidas, ahora es abrir cuatro archivos. Y aparece un lugar nuevo donde equivocarse: el punto que decide qué regla corresponde a qué boleto.
Cuándo el intercambio vale la pena: cuando hay varias variantes reales —no imaginadas—, cuando siguen apareciendo, y cuando cada una tiene lógica propia y no trivial. En Boletia hay cuatro, van a ser seis, y dos de ellas (early-bird y cortesía) ya traen reglas de fecha y de tope. La Strategy se gana su lugar.
Cuándo no: si solo hubiera dos tipos, ambos de una línea, y ninguno más en el horizonte, la misma estructura sería puro costo. Seis archivos para lo que cabía en un if de dos ramas. El módulo 2 entero se ocupa de este caso, que es más frecuente de lo que la industria admite.
Qué esperar de este ejercicio. Tres cosas.
La primera: fíjate que la casilla 3 fue la más larga. Eso no es casualidad ni relleno. En una decisión real de diseño, la conversación pasa casi toda ahí. Las casillas 1 y 2 se resuelven en un minuto entre dos personas que comparten vocabulario; la casilla 3 es donde el equipo discute.
La segunda: fíjate que la casilla 3 no da un veredicto. Da un intercambio, y después una condición bajo la cual ese intercambio conviene. Eso significa que el mismo patrón, sobre el mismo código, puede ser correcto o incorrecto según el contexto. Los patrones no son buenos ni malos; son caros o baratos según lo que compres con ellos.
Y la tercera, quizás la más práctica: cuando alguien te presente un patrón —en un video, en un artículo, en una revisión de código— y no te diga la casilla 3, la explicación está incompleta. No es que sea mala: es que le falta la mitad de lo que necesitas para decidir. Pregúntala. "¿Y qué pagamos con esto?" es la mejor pregunta que puedes hacer en una discusión de diseño.
Las tres partes, una por una
Ya las viste en acción. Vamos a mirarlas de frente, porque cada una tiene sus trampas.
Parte 1: el problema
El problema es la razón de existir del patrón, y también es la parte que se lee más rápido y se entiende peor.
La trampa es que las descripciones de patrones suelen enunciar el problema en un lenguaje muy abstracto, heredado del libro original: "define una familia de algoritmos, encapsula cada uno y hazlos intercambiables". Eso no es un problema; es un resumen de la solución escrito con palabras de problema. Alguien que lee eso no sabe todavía qué incomodidad concreta se está resolviendo.
Un problema bien enunciado suena a queja, no a arquitectura. Algo como:
- "Cada vez que agregamos una forma de cobrar, hay que editar el archivo más delicado del sistema."
- "Esta clase tiene ocho parámetros en el constructor y la mitad son opcionales; nadie sabe cuáles son obligatorios."
- "La librería del proveedor nos obliga a hablar su idioma en veinte lugares distintos; si la cambiamos, tocamos veinte lugares."
- "Cuando alguien compra, hay cinco cosas que tienen que enterarse, y la función de compra las conoce a las cinco por nombre."
Fíjate que todas tienen la misma forma general: algo varía, y la variación se está filtrando a lugares donde no debería importar. Casi todos los patrones estructurales y de comportamiento atacan alguna versión de eso. Si aprendes a formular problemas así —en lenguaje de dolor concreto, no de arquitectura— vas a reconocerlos mucho mejor en código ajeno. Esa es, de hecho, la técnica de la lección 5.
Una prueba útil: si puedes describir el problema sin usar el nombre del patrón, lo entendiste. Si necesitas decir "el problema es que no tenemos una Strategy", no lo entendiste: describiste la ausencia de la solución, que es otra cosa.
Parte 2: la solución estructural
Es la parte fácil de estudiar y la que más se sobrevalora. Es el diagrama, las clases, las flechas. Es lo que aparece en el video de diez minutos.
Dos matices sobre esta casilla que ahorran confusión.
La solución es una forma, no un código. Un patrón no es una librería que instalas ni una clase que copias. Es una manera de acomodar piezas. Por eso el mismo patrón se ve distinto en Python, en Java y en Go —a veces muy distinto—, y por eso hay patrones que en ciertos lenguajes prácticamente desaparecen. La Strategy de nuestro ejemplo, en Python, podría implementarse sin una sola clase: un diccionario que mapea kind a función, y listo. Sigue siendo la misma idea —separar el comportamiento variable, darle una forma común, elegir cuál usar— pero con una décima parte de la ceremonia. El módulo 3 le dedica una lección a esa frontera.
La solución casi nunca elimina la complejidad: la mueve. Este punto es tan importante que conviene repetirlo. Cuando pasas de un if/elif a cuatro clases, la decisión "cuál corresponde" no desapareció: se mudó. Antes vivía en un condicional dentro del calculador; ahora vive en un punto de selección —un diccionario, una fábrica, un campo del objeto—. Lo que ganaste es que ahora vive en un solo lugar y el resto del código no la ve. Eso es valioso. Pero no es magia, y quien crea que un patrón hace desaparecer complejidad va a decepcionarse.
Parte 3: las consecuencias
Aquí vive el criterio. Y aquí es donde esta guía se separa de casi todo el material que vas a encontrar.
Una casilla de consecuencias completa tiene cuatro renglones:
Lo que ganas. Normalmente: cierto tipo de cambio se vuelve barato. Ojo con la palabra "cierto": un patrón no hace el código genéricamente mejor. Hace barato un tipo específico de cambio y, con frecuencia, hace más caro otro. La Strategy de precios abarata "agregar un tipo de boleto" y encarece "cambiar la firma del cálculo para todos los tipos a la vez".
Lo que pagas. Casi siempre lo mismo, con distintos disfraces: indirección. Más archivos, más saltos para seguir el flujo, más nombres que aprender, más superficie donde equivocarse. La indirección es el impuesto universal de los patrones, y se paga en la moneda más escasa que tiene un equipo: la atención de quien lee el código. Alguien tiene que entender la estructura antes de poder cambiar cualquier cosa, y ese alguien puede ser tú dentro de ocho meses.
Cuándo conviene. Las condiciones bajo las cuales el intercambio es bueno. Casi siempre involucra un número: cuántas variantes hay hoy, cuántas se esperan, qué tan seguido cambia esto. La regla de tres del módulo 2 es una respuesta empaquetada a esta pregunta.
Cuándo no. Las condiciones bajo las cuales el mismo patrón es un error. Esta parte casi nunca se enseña y es la mitad del valor. Cada patrón de esta guía va a venir con su "cuándo no", explícito.
Por qué la tercera casilla es la que casi nadie estudia
Vale la pena entender por qué se salta, porque saberlo te protege.
Porque no se puede dibujar. El problema se puede contar en una escena. La solución se puede diagramar. Las consecuencias son prosa, matices y condicionales. En un formato visual —una infografía, una tarjeta de repaso, un video corto— las consecuencias son lo primero que se recorta.
Porque no se puede evaluar en un examen. "¿Cuáles son las partes de un patrón Observer?" tiene respuesta correcta. "¿Conviene un Observer aquí?" tiene respuesta depende, y las respuestas depende no entran en un formulario de opción múltiple. Por eso los cursos evalúan la casilla 2.
Porque solo se aprende con tiempo. El costo de la indirección no se siente el día que escribes el patrón. Se siente meses después, cuando alguien —a veces tú— intenta seguir el flujo y no puede. Quien nunca mantuvo código a un año de escrito no ha sentido esa factura, y por eso la subestima honestamente.
Y porque decir "sí" se siente más profesional que decir "no". Introducir una abstracción parece trabajo de ingeniería. Decir "esto está bien como está" parece pereza. Esa asimetría social es real y empuja a todo el mundo en la misma dirección. Una de las metas de esta guía es darte los argumentos para que decir "aquí no" suene tan profesional como es.
Hay una consecuencia práctica de todo esto que quiero dejarte como hábito. Cuando estudies un patrón nuevo —de esta guía o de cualquier lado—, después de entender la solución hazte estas dos preguntas antes de seguir:
- "¿Qué tipo de cambio se vuelve caro con esto puesto?"
- "¿Cuántas variantes tendría que haber para que valga la pena?"
Si no puedes contestar ninguna de las dos, todavía no sabes el patrón: sabes su dibujo.
Cómo leer la ficha de cualquier patrón
Esta plantilla te va a servir el resto de tu carrera, incluso con patrones que esta guía no cubre. Cuando te encuentres una ficha de patrón en cualquier fuente, léela en este orden y llena estas casillas:
| Casilla | La pregunta que responde | Cómo saber que la llenaste bien |
|---|---|---|
| Problema | ¿Qué incomodidad concreta hay hoy? | Puedes describirla sin nombrar el patrón, y suena a queja de alguien que mantiene el código |
| Solución | ¿Cómo se acomodan las piezas? | Puedes dibujarla en una servilleta y decir dónde se movió la decisión |
| Ganas | ¿Qué tipo de cambio se vuelve barato? | Nombras un cambio específico, no "el código queda más limpio" |
| Pagas | ¿Cuánta indirección agregas? | Puedes contar archivos y saltos: "pasa de 1 archivo a 6, de 0 saltos a 3" |
| Conviene si | ¿Cuántas variantes reales hay? | Tu respuesta tiene un número, no un adjetivo |
| No conviene si | ¿En qué escenario esto es un error? | Puedes describir un caso donde tú mismo lo rechazarías |
Un detalle sobre la fila "Ganas". Cuando alguien justifica un patrón diciendo "queda más limpio" o "es más elegante", no ha llenado la casilla. Limpio y elegante no son consecuencias: son opiniones. La consecuencia es "agregar un proveedor de pago deja de tocar checkout.py". Esa frase se puede verificar, se puede discutir, y se puede refutar si alguien demuestra que en la práctica igual hay que tocarlo. Cámbiate al hábito de justificar así, y vas a ganar la mitad de las discusiones de diseño en las que participes —no por tener razón, sino por ser el único que dijo algo comprobable—.
Otro detalle, sobre la fila "Pagas". Fíjate que la sugerencia es contar: archivos, saltos, nombres nuevos. Los costos de la indirección se sienten difusos, y por eso pierden contra los beneficios, que se enuncian concretos. Volverlos numéricos empareja la discusión. En Boletia, la Strategy de precios pasa de 1 archivo a 6 y de 0 saltos a 3 para responder "¿cuánto cuesta un VIP?". Con esos dos números sobre la mesa, la conversación es honesta.
Errores comunes
Estudiar la solución y saltarse las consecuencias (de estudio). Qué pasa: alguien ve el diagrama de un patrón, entiende cómo se arma, escribe una versión de juguete, y se da por satisfecho. En el trabajo lo aplica y produce código que técnicamente está bien pero que nadie quería. Por qué pasa: la solución es la parte visual, evaluable y satisfactoria; las consecuencias son prosa con matices. Además, la factura de la indirección llega meses tarde, así que quien no ha mantenido código no la ha sentido. Cómo detectarlo: hazte la prueba de las dos preguntas —"¿qué se vuelve caro con esto puesto?" y "¿cuántas variantes harían falta?"—. Si no las contestas, sabes el dibujo y no el patrón. Cómo corregirlo: por cada patrón que estudies, escribe su casilla 3 con tus palabras antes de escribir una línea de código. Esta guía te la va a dar siempre, pero el hábito de exigirla vale más que la que yo te dé.
Confundir el problema con la ausencia de la solución (conceptual). Qué pasa: alguien dice "el problema aquí es que no hay una Factory". Eso no describe ningún problema: describe que falta algo que quizás no hace falta. Es una petición de principio disfrazada de diagnóstico. Por qué pasa: cuando aprendes un patrón nuevo, el patrón se vuelve la lente por la que ves todo, y el código sin él parece incompleto. Es el síndrome del martillo nuevo, que la lección 7 trata en serio. Cómo detectarlo: intenta enunciar el problema sin usar el nombre del patrón. Si no puedes, no hay problema identificado. Cómo corregirlo: formula siempre en lenguaje de dolor concreto —"cada vez que X, tenemos que tocar Y"—. Si no encuentras ningún "cada vez que", puede que no haya nada que resolver, y esa es una conclusión perfectamente válida.
Creer que el patrón elimina la complejidad (conceptual). Qué pasa: alguien introduce una Strategy y espera que el sistema quede más simple. Queda distinto: la decisión se mudó y aparecieron archivos. Cuando el resultado no se siente más simple, la conclusión equivocada es "lo implementé mal" y se agrega más estructura encima. Por qué pasa: las explicaciones de patrones hablan de "eliminar condicionales" y eso suena a que algo desaparece. No desaparece: se convierte en polimorfismo o en una tabla de despacho, y vive en otro lado. Cómo detectarlo: después de aplicar un patrón, pregúntate dónde quedó la decisión que antes estaba en el if. Si no puedes señalar el lugar, no entendiste tu propio cambio. Cómo corregirlo: piensa en los patrones como redistribución de complejidad, no como reducción. La pregunta correcta no es "¿queda más simple?" sino "¿queda la complejidad en un lugar donde molesta menos?".
Ejercicios
Ejercicio 1 — Llena las tres casillas sin nombrar ningún patrón. Mira este fragmento de reports/ de Boletia. Escribe la casilla 1 (el problema, en lenguaje de queja concreta), la casilla 2 (qué cambio estructural propondrías, descrito en prosa) y al menos dos renglones de la casilla 3 (qué ganas y qué pagas). Prohibido usar el nombre de ningún patrón en toda la respuesta.
# Archivo: reports/exporter.py (versión actual, antes de que existieran los tres archivos)
def export_attendees(event_id, fmt):
rows = db.fetch_attendees(event_id) # 1. traer los datos
rows = sorted(rows, key=lambda r: r["name"]) # 2. ordenarlos
if fmt == "csv":
body = ",".join(HEADERS) + "\n"
for r in rows:
body += ",".join(str(r[h]) for h in HEADERS) + "\n"
path = f"/tmp/attendees_{event_id}.csv"
write_text(path, body) # 4. escribir el archivo
return path
elif fmt == "pdf":
doc = PdfDocument(title=f"Asistentes evento {event_id}")
for r in rows:
doc.add_row([str(r[h]) for h in HEADERS])
path = f"/tmp/attendees_{event_id}.pdf"
doc.save(path)
return path
elif fmt == "xlsx":
book = Workbook()
sheet = book.active
sheet.append(HEADERS)
for r in rows:
sheet.append([r[h] for h in HEADERS])
path = f"/tmp/attendees_{event_id}.xlsx"
book.save(path)
return path
else:
raise ValueError(f"Formato desconocido: {fmt}")
Ver solución
Casilla 1 — el problema. Los tres caminos hacen exactamente lo mismo en el mismo orden: traer los datos, ordenarlos, darles formato, escribir el archivo. Solo cambian los pasos 3 y 4 —cómo se le da formato y cómo se guarda—. Los pasos 1 y 2 están escritos una vez, bien, pero el resto de la función repite el esqueleto tres veces. Consecuencia concreta: si mañana hay que filtrar a los asistentes cancelados —un cambio en el paso 1— hay que revisar los tres caminos para asegurarse de que ninguno se quedó atrás. Y agregar un cuarto formato obliga a copiar el esqueleto una cuarta vez, con la tentación de copiar y pegar.
Un segundo dolor, más silencioso: hoy la ruta del archivo se arma en tres lugares distintos con la misma fórmula. Si cambia la convención de nombres, hay tres lugares que actualizar y ninguno avisa si se te olvida uno.
Casilla 2 — la solución. Escribir el esqueleto una sola vez —traer, ordenar, formatear, escribir— y dejar como huecos rellenables los dos pasos que varían. Cada formato aporta solo su forma de dar formato y su extensión de archivo; nadie repite el orden general. Quien agrega un cuarto formato escribe únicamente lo que es propio de ese formato, y hereda el resto gratis.
(Si dijiste "sacar cada formato a su propio objeto con la misma forma" también es una respuesta válida y describe una estructura ligeramente distinta. Las dos son legítimas y en el módulo 3 vamos a ver que tienen nombres diferentes y consecuencias diferentes. Que hayas llegado a una u otra sin conocer los nombres es exactamente el punto del ejercicio.)
Casilla 3 — consecuencias.
Ganas: un cambio al esqueleto —agregar un filtro, cambiar el orden, agregar un registro de auditoría— se hace en un solo lugar y aplica a los tres formatos automáticamente. Agregar un cuarto formato deja de implicar copiar y pegar. Y la convención del nombre de archivo vive en un solo sitio.
Pagas: leer el flujo completo de una exportación en PDF ya no es leer una rama de arriba a abajo; hay que leer el esqueleto en un lugar y el paso propio en otro. Aparece un acoplamiento nuevo: los tres formatos quedan atados a un esqueleto compartido, así que si mañana uno de ellos necesita un orden distinto —por ejemplo, que el PDF sí quiera los cancelados— ese caso especial se vuelve incómodo y hay tentación de meterle un parámetro al esqueleto, que es como empiezan a pudrirse estas cosas.
Conviene si: los tres formatos de verdad comparten el esqueleto —hoy sí— y se esperan más. No conviene si: alguno de los formatos empieza a divergir en el flujo general, porque entonces el esqueleto compartido se llena de banderas y estorba más de lo que ayuda.
Por qué funciona: acabas de hacer diseño de software sin conocer un solo nombre de patrón. Ese es el orden correcto —del problema al nombre, nunca al revés— y es lo que la lección 5 va a convertir en técnica.
Ejercicio 2 — Cuenta el costo. Vuelve a la propuesta de Strategy sobre pricing/ que vimos en el ejemplo trabajado. Responde con números, no con adjetivos: (a) ¿de cuántos archivos a cuántos pasa la lógica de precios? (b) para contestar "¿cuánto cuesta un boleto VIP?", ¿cuántos archivos hay que abrir antes y después? (c) enuncia un tipo de cambio que se vuelve más caro con la Strategy puesta.
Ver solución
(a) De 1 archivo (pricing/calculator.py) a 6: el calculador, la definición de la forma común PricingRule, y una clase por cada uno de los cuatro tipos. Si el punto de selección vive aparte, son 7.
(b) Antes: 1 archivo. Abres calculator.py, buscas la rama vip, lees una línea. Después: 3. Abres calculator.py y ves que recibe una regla; buscas quién decide la regla para un boleto VIP; abres VipPricing y lees la línea. La línea que buscabas es la misma; el camino para llegar a ella es tres veces más largo.
(c) Varios cambios se encarecen. El más claro: cambiar la firma del cálculo para todos los tipos a la vez. Supón que mañana el precio también depende de la moneda del comprador. Con el if/elif es un parámetro más en una función y cuatro ramas que ajustar, todo en un archivo, con el compilador o el editor mostrándote todo junto. Con la Strategy hay que cambiar la forma común y las cuatro implementaciones, en cinco archivos, y asegurarse de que ninguna quedó desincronizada.
Otro que se encarece: comparar las cuatro reglas entre sí. La pregunta "¿el recargo del VIP es mayor que el descuento del early-bird?" se contestaba leyendo treinta líneas seguidas. Ahora requiere abrir cuatro archivos y sostener las cuatro respuestas en la cabeza.
Por qué funciona: el hábito de contar archivos y saltos convierte un costo difuso en un dato. Y con un dato sobre la mesa, la discusión de diseño deja de ser una pelea de gustos.
Ejercicio 3 — Encuentra un patrón fuera del software. Elige una actividad no técnica que domines. Identifica una "solución con nombre a un problema que se repite" dentro de esa actividad y escribe su ficha completa: problema, solución, qué ganas, qué pagas, cuándo conviene y cuándo no. Después responde: ¿la persona que te enseñó esa técnica te contó la casilla de las consecuencias, o la aprendiste por tu cuenta?
Ver solución
No hay respuesta única. Lo que sí es predecible es el hallazgo.
Un ejemplo, por si sirve de molde. En cocina, el baño maría. Problema: hay preparaciones que se cortan o se queman si reciben calor directo —una salsa con yema, chocolate—, y necesitas calor suave y parejo que no pase de cierta temperatura. Solución: poner el recipiente sobre agua caliente en vez de sobre la llama; el agua no pasa de su punto de ebullición, así que el contenido tampoco. Ganas: control, imposible quemarlo, resultado reproducible sin vigilar cada segundo. Pagas: es más lento, ocupa dos trastes, y hay que cuidar que no entre agua ni vapor donde no debe. Conviene si: el ingrediente es delicado y el margen entre "listo" y "arruinado" es angosto. No conviene si: lo que estás calentando aguanta fuego directo, en cuyo caso el baño maría es ceremonia y tiempo perdido.
Y sobre la última pregunta, la respuesta de la mayoría es la misma: la casilla de consecuencias no te la contaron; la aprendiste arruinando algo. Quien te enseñó te dijo el problema y el procedimiento. El "cuándo no conviene" lo descubriste el día que hiciste baño maría para algo que no lo necesitaba y perdieron veinte minutos, o el día que se te metió agua.
Por qué funciona: si en un oficio con siglos de tradición las consecuencias se transmiten mal, imagínate en uno de cincuenta años. Que esta guía insista tanto en la tercera casilla no es una manía: es la parte que sistemáticamente se pierde en la transmisión.
Resumen y siguiente paso
En esta lección le pusimos precisión a la idea de la lección 1. Un patrón de diseño es una solución con nombre a un problema de estructura que se repite, junto con las consecuencias de aplicarla, y cada palabra de esa frase está ahí por algo. Usamos los nudos de un marinero para ver las tres partes —para qué sirve, cómo se hace, qué pasa cuando lo usas— y las llenamos sobre el cálculo de precios de Boletia sin implementar nada.
Viste que la casilla del problema se enuncia en lenguaje de queja concreta y no de arquitectura, y que si no puedes describirla sin nombrar el patrón, no la entendiste. Viste que la casilla de la solución es una forma y no un código, que cambia de aspecto según el lenguaje, y que casi nunca elimina complejidad: la mueve a un lugar donde molesta menos. Y viste que la casilla de las consecuencias —la que casi nadie estudia, porque no se dibuja, no se evalúa y su factura llega tarde— es donde vive todo el criterio, con sus cuatro renglones: qué ganas, qué pagas, cuándo conviene, cuándo no.
Antes de avanzar deberías poder: dar la definición de patrón en una frase; enunciar el problema del cálculo de precios de Boletia sin decir "Strategy"; y contar en archivos y saltos lo que cuesta una abstracción concreta.
Lo que sigue es la parte del "nombre", que hasta ahora dimos por buena sin examinarla. La lección 3 se pregunta en serio por qué nombrar algo cambia tanto: qué le pasa a una conversación de equipo cuando dos personas comparten vocabulario, por qué eso vale más que saber implementar el patrón, y —el lado incómodo— qué destrozos hace un nombre usado mal. Ahí está, en mi opinión, el verdadero superpoder de todo esto.
Recursos
- Refactoring Guru — Strategy — buena ficha para practicar la plantilla de esta lección: identifica cuáles de las tres casillas están completas y cuál queda corta.
- Design Patterns: Elements of Reusable Object-Oriented Software (Gamma et al.) — el libro original organiza cada patrón exactamente con estas secciones (Intent, Motivation, Consequences). Vale la pena hojear una ficha solo para ver la estructura.
- Refactoring: Improving the Design of Existing Code (Martin Fowler) — cada refactor del catálogo viene con su "Motivation" y sus contraindicaciones; es la mejor escuela disponible para escribir buenas casillas 3.
- Choose Boring Technology (Dan McKinley) — no habla de patrones, pero es la mejor pieza corta sobre pagar el costo de la complejidad solo cuando compras algo con ella.