Patrones que se Confunden entre Sí
Wrappers, fábricas, algoritmos intercambiables, notificaciones y undo: los cinco grupos de patrones con estructura casi idéntica, y qué pregunta los distingue en cada caso.
Buena parte del catálogo GoF comparte estructura. Cuatro patrones envuelven un objeto y delegan; tres crean objetos detrás de una abstracción; dos delegan comportamiento a un objeto intercambiable. Mirando solo el diagrama de clases, varios son indistinguibles.
Lo que los separa no es la estructura sino la intención: qué problema estaban resolviendo cuando alguien decidió usarlos. Este artículo recorre los cinco grupos donde esa confusión aparece más seguido y da, para cada uno, la pregunta que los desambigua.
Los cuatro wrappers: Adapter, Decorator, Proxy y Facade
Los cuatro reciben un objeto, guardan una referencia y delegan. La diferencia está en qué cambian al delegar.
| Patrón | ¿Cambia la interfaz? | ¿Cambia el comportamiento? | ¿Cuántos objetos envuelve? | Intención |
|---|---|---|---|---|
| Adapter | Sí | No | Uno | Hacer compatible lo que no lo era |
| Decorator | No | Sí, agrega | Uno | Sumar responsabilidades sin heredar |
| Proxy | No | No, controla el acceso | Uno | Decidir si y cuándo se llega al objeto real |
| Facade | Sí, la simplifica | No | Varios | Dar una entrada simple a un subsistema |
La pregunta que ordena el grupo:
- ¿La interfaz de salida es distinta de la de entrada? Si sí, es Adapter o Facade. Si envuelve un objeto para traducirlo, Adapter; si envuelve varios para simplificar, Facade.
- ¿La interfaz es la misma? Entonces es Decorator o Proxy. Si el wrapper agrega comportamiento observable (un precio extra, un log, una validación), es Decorator. Si el comportamiento es idéntico y lo que el wrapper decide es el acceso (cargar de forma lazy, cachear, verificar permisos, llamar a un objeto remoto), es Proxy.
Las tres fábricas: Factory Method, Abstract Factory y la “Simple Factory”
Este es el grupo donde más se confunde el nombre, en parte porque el tercero no es un patrón GoF.
| Qué produce | Cómo se elige la variante | ¿Es GoF? | |
|---|---|---|---|
| Simple Factory | Un producto | Un switch o if dentro de un método estático | No, es un idiom |
| Factory Method | Un producto | Eligiendo la subclase del creador | Sí |
| Abstract Factory | Una familia de productos compatibles | Eligiendo la fábrica concreta | Sí |
Un método estático con un switch que devuelve new PdfExporter() o new CsvExporter() es útil y perfectamente legítimo, pero no es Factory Method. En Factory Method la decisión está codificada en la jerarquía: cada subclase del creador sabe qué producto le corresponde, y no hay condicional que actualizar al sumar una variante.
La pregunta que ordena el grupo:
- ¿Cuántos productos relacionados se crean juntos? Si es uno solo, mirá Factory Method. Si son varios que tienen que ser compatibles entre sí (una factura y una etiqueta, ambas del mismo país), es Abstract Factory.
- ¿La variante se elige extendiendo una clase o con un condicional? Si es un condicional en un solo lugar y no molesta, es una simple factory y está bien. Llamala así.
Delegar comportamiento: Strategy, State y Template Method
Strategy y State tienen diagramas de clases prácticamente idénticos: un contexto con una referencia a una interfaz, y varias implementaciones. Se distinguen por quién decide cuál se usa.
| Patrón | Quién elige la implementación | ¿Las implementaciones se conocen? | Cuándo cambia |
|---|---|---|---|
| Strategy | El código cliente | No, son independientes | Cuando el cliente lo decide |
| State | Los propios estados | Sí, cada uno sabe a cuál transiciona | Como consecuencia de una operación |
| Template Method | Se fija al elegir la subclase | Irrelevante | No cambia en runtime |
La pregunta decisiva: ¿una implementación puede reemplazarse a sí misma por otra? Si PaidState decide pasar a ShippedState, es State. Si ExpressShipping no tiene idea de que existe StandardShipping, es Strategy.
La otra distinción, entre los dos primeros y Template Method, es composición contra herencia: Strategy y State se cambian en tiempo de ejecución porque son objetos referenciados; Template Method queda fijo porque la variación se resuelve al instanciar la subclase.
Comunicar cambios: Observer y Mediator
Los dos reducen el acoplamiento entre objetos que necesitan reaccionar unos a otros, y los dos terminan con un objeto central. La diferencia es qué sabe ese objeto central.
- En Observer el flujo es unidireccional y anónimo: el subject anuncia que algo pasó y no sabe ni le importa quién escucha. Los observers no se conocen entre sí ni coordinan nada. Sumar un observer no cambia el subject.
- En Mediator el flujo es bidireccional y coordinado: el mediador conoce a todos los colleagues y contiene la lógica de cómo se afectan entre sí. Cuando cambia el método de envío, el mediador decide recalcular el total y deshabilitar el cupón.
La pregunta: ¿el objeto central tiene reglas? Si solo reparte el aviso, es Observer. Si decide qué hacer en función de quién avisó, es Mediator. Un mediator suele usar observer internamente para enterarse de los cambios; son complementarios más que alternativos.
Guardar y deshacer: Command y Memento
Se confunden porque los dos aparecen cuando alguien pide “deshacer”.
- Command guarda la operación: qué se hizo y con qué argumentos. Deshacer es aplicar la operación inversa. Es eficiente en memoria y permite además encolar, reintentar y loguear.
- Memento guarda el estado: una foto de cómo estaba el objeto antes. Deshacer es restaurar la foto. Es más simple de escribir correctamente, y más caro si el estado es grande.
En la práctica se combinan: un Command guarda un Memento del estado previo cuando calcular la operación inversa es difícil o imposible.
Cómo usar esta comparación
Ninguna de estas preguntas sirve para decidir qué patrón aplicar a un problema nuevo. Sirven para lo contrario: nombrar correctamente algo que ya escribiste, o entender qué esperaba quien escribió el código que estás leyendo.
Para elegir, el camino sigue siendo el del artículo de introducción: partir del problema concreto, no del catálogo.
