Los Patrones de Diseño Hoy: Patrones de Comportamiento

Estoy dedicando esta serie de post a ver cómo han resistido los patrones de diseños clásicos, los del libro original de la GoF, el paso del tiempo. En los posts anteriores hablábamos sobre los patrones de diseño creacionales y los patrones estructurales, y en éste toca terminar la serie con los patrones de diseño de comportamiento.

Chain of Responsibility

Es un patrón que estructuralmente está a medio camino entre Decorator y Composite: por una parte cada elemento de la cadena «decora» al elemento posterior, y por otra cada elemento contiene otro objeto similar a él.

Pese a mi gusto por Decorator y Composite, Chain of Responsibility no me acaba de convencer. No me gusta la idea de mezclar al estructura de los componentes implicados en un proceso, con la propia lógica del proceso.

En ese sentido, creo que es preferible tener interfaces con una signatura del estilo:

interface Handler {
  bool CanHandle(Request request);
  void Handle(Request request);
}

Y dejar que sea otro objeto distinto el que decida cómo componer esos objetos y bajo qué reglas.

Utilidad hoy en día: 2.

Command

Uno de los patrones que más utilizo en sus distintas variantes. Tal vez la idea original de tener una pila de commands con acciones compensatorias para poder hacer Undo/Redo no sea un caso muy frecuente (a menos que estés haciendo algún tipo de editor), pero empaquetar datos con la acción a realizar, ya sea mediante clases o mediante lambdas, sigue siendo extremadamente útil y rara es la aplicación en que no lo acabo utilizando para algo.

Utilidad hoy en día: 5.

Interpreter

Es raro tener que implementar este patrón, a menos que necesites crear tu propio lenguaje. Hubo una época en la que los DSLs externos tuvieron cierto auge, y puede que en ciertos lenguajes se usen más, pero no me lo suelo encontrar mucho en mi día a día.

En lenguajes como Clojure se utiliza frecuentemente una variante de este patrón en el que se emplean estructuras de datos que luego son interpretadas para dar lugar a operaciones más complejas u otras estructuras de datos diferentes, como se ve en hiccup para generar HTML o en Datomic para lanzar consultas.

Por supuesto, hay escenarios donde resulta muy útil, como las consultas HQL de NHibernate o los selectores CSS extendidos que soporta jQuery.

Utilidad hoy en día: 2.

Iterator

Te lo encuentras continuamente. Incluso Javascript lo soporta en sus últimas versiones. Encaja muy bien con la idea de poder recorrer cosas como si fuesen secuencias, sin preocuparte de su estructura interna, y eso hace que sea especialmente útil para ese estilo «funcional» que cada vez se ve en más lenguajes.

Utilidad hoy en día: 5.

Mediator

Al principio me costaba reconocer este patrón, pero luego lo he encontrado en muchos sitios. Si utilizas C#, seguro que te suena la librería Mediatr para comunicar componentes dentro de una misma aplicación. El clásico EventBroker del Composite Application Block o el Dispatcher de Flux son otros ejemplos de este patrón.

Me gusta porque ayuda a generar un código homogéneo y limpio, desacoplando mucho unos componentes de otros y facilitando la creación de puntos de intercepción en los que introducir lógica global para cosas como logging, transaccionalidad, seguridad, etc.

A cambio, a veces está todo tan desacoplado que el código pierde linealidad y se hace más complicado de seguir. Cuidado con abusar mucho de esto.

Utilidad hoy en día: 4.

Memento

La parte de poder guardar el estado de un objeto para recuperarlo posteriormente sigue siendo útil, y hoy en día se usa bastante incluso en aplicaciones web, donde se persiste a LocalStorage el estado de la aplicación para poder recuperarlo sin necesidad de pasar por el servidor.

Lo que no me gusta de este patrón en su formulación clásica es que hace que sea el propio objeto cuyo estado queremos persistir el responsable de generar la información persistible. Por una parte está bien porque permite mantener la encapsulación, pero por otra acerca ese objeto a un god object y a una violación del Single Responsibility Principle.

Utilidad hoy en día: 3.

Observer

Sigue siendo un patrón básico en la construcción de interfaces de usuarios. Da igual que sea con dirty checking como AngularJS, con Stores al estilo Flux, con Reactive Programming, o con el infame INotifyPropertyChanged de XAML & Friends, casi todos los frameworks y librerías de UI incluyen su versión de este patrón.

Utilidad hoy en día: 5.

State

Suena muy bien. En lugar de tener un montón de ifs y código condicional para gestionar el comportamiento de un objeto cuando pasa por distintos estados, extraes distintas clases para representar cada estado y queda todo mucho más limpio y bonito.

Lo malo es que, casi siempre, aplicar este patrón acaba llevando a violaciones del Principio de Sustitución de Liskov porque habrá operaciones no permitidas en ciertos estados que nos obligarán a lanzar excepciones.

Si el lenguaje lo permite, me gusta más la opción de utilizar tipos algebraicos para representar los distintos estados. Si no, intento modelar de forma que pueda usar clases completamente diferentes sin recurrir a estados; por ejemplo, en lugar de tener una clase Order que represente un pedido que puede estar pendiente de facturar o cancelado, prefiero hacer que la clase Order siempre represente un pedido pendiente de facturar y tener una clase Cancellation completamente independiente para representar la cancelación.

Utilidad hoy en día: 2.

Strategy

Es primo hermano del patrón estado, pero éste sí que me gusta. A diferencia del estado, no nos obliga a implementar un montón de métodos que se supone que no se van a llamar y por tanto acaban lanzando NotSupportedExceptions.

Combinado con funciones permite crear dinámicamente estrategias para inyectar comportamiento personalizado en clases existentes.

Resulta útil para evitar situaciones en las que se aplica herencia sólo para reutilizar código, y para no tener que definir trescientas clases con ligeras variaciones de comportamiento.

Utilidad hoy en día: 4.

Template Method

Es un patrón basado en herencia, y eso ya tira un poco para atrás hoy en día por el alto acoplamiento que implica. Aun así, se utiliza en muchos frameworks y librerías de diversa índole (AngularJS, React, WPF, Entity Framework…).

Reconozco que me gusta bastante, pese al inconveniente de la herencia, porque es extremadamente cómodo definir una clase con la estructura del algoritmo, un par de métodos abstractos, y empezar a crear implementaciones como churros. Sí, se podría hacer con otros patrones como Strategy (y probablemente muchas veces fuera mejor idea), pero acabo usando éste por comodidad.

Utilidad hoy en día: 4.

Visitor

Me encantaba este patrón. Era uno de mis favoritos. La parte del double dispatch que se hace entre los elementos visitados y el Visitor para invocar el método adecuado siempre me pareció una idea genial.

La verdad es que al final es un patrón que conlleva un acoplamiento enorme entre los Visitors y los visitados. Cada vez que añades un nuevo tipo de elemento a visitar, obliga a tocar todos los visitors que ya existen. La salida a eso suele ser tener una clase base de visitors que amortigüe el impacto.

Existe una variante basada en un mega switch, como el usado en System.Linq.ExpressionVisitor, que es más fea pero es algo más simple y se ajusta bastante bien al tipo de problemas que se suelen resolver con este patrón.

Utilidad hoy en día: 2.

Conclusión

En el caso de los patrones de comportamiento hay un poco de todo. Algunos siguen siendo muy útiles, como Command, o Mediator, y otros podríamos decir que violan los (tan queridos por algunos) principios SOLID, como State y Memento.

En general, lo interesante de los patrones de diseño clásicos siguen siendo las ideas que hay detrás. Si en lugar de centrarte en memorizar los diagramas UML que hay en cientos de páginas intentas comprender qué hay detrás de cada patrón, qué problema tratan de resolver y cómo lo hacen, yo diría que todos los patrones de diseño clásico siguen teniendo validez hoy en día.

Por desgracia, muchas veces se intentan meter con calzador sólo por poder decir «yo uso patrones de diseño», y además se sigue una guía de implementación pensada hace más de 20 años para un lenguaje diferente al que que estás usando ahora.

Afortunadamente, esto es sólo una fase y la mayoría de nosotros conseguimos superarla y sacarle partido a los patrones de diseño como lo que son: un catálogo de ideas para resolver problemas que nos proporcionan un lenguaje común para comunicarnos con otros desarrolladores.

2 comentarios en “Los Patrones de Diseño Hoy: Patrones de Comportamiento

  1. No estoy tan seguro de la utilidad de los patrones como descripciones genericas de practicas comunes, ni siquiera por su utilidad de tener un vocabulario comun entre desarrolladores.
    Para empezar no son tan «genericos», ya que estos patrones se dan en un contexto bastante concreto: lenguajes orientado a objetos con tipado estaico limitado y sin soporte adecuado a otros paradigmas (programacion funcional). Ademas en su formulacion y uso no se tuvo demasiado en cuenta los fundamentos matematicos y logicos de la ciencias de la computacion, ingenieria informatica o como le llamemos. Se hizo un catalogo ad-hoc de «buenas» practicas en ese contexto, con el pecado original de la arrogancia (no se si os acordais) de que se habia llegado con ellos a la culminacion del desarrollo del software como algo artesanal.

    Por tanto son utiles (pero tambien peligrosos por la deformacion) en la medida que estos patrones repiten o reinterpretan ideas previas no atadas a ese contexto. Para ser honestos, el mismo GoF declara el contexto de aplicacion, aunque en su formulacion olvidaran parte de la investigacion teorica previa ya que hacia casi futil su tarea.

    Desde ese punto de vista, curiosamente, el patron mas generico y que mas pegado esta a esos fundamentos y el mas vigente de todos en mi opinion es el interpreter, que se ha valorado negativamente debido al abuso del mismo en los dsl’s que acabaron siendo capas de azucar sintactica demasiado empalagosa.

    Es el patron fundamental de cualquier lisp y de las free monads, que lo redefine en terminos realmente genericos
    https://www.youtube.com/watch?v=hmX2s3pe_qk

  2. Sergio León dijo:

    Hola Juanma,

    Muy buena serie de posts, excelente repaso.

    Coincido contigo en que lo importante es tener clara la intención de cada patrón, luego y partiendo de esa base se adapta a tu caso concreto o directamente se toma prestado la idea y cualquier parecido con la realidad es pura ficción (reconozco que durante un tiempo me costó mucho reconocer Builder o Command porque siempre esperaba encontrar su UML en el código y nunca lo encontraba).

    Mis preferidos (o al menos los que más uso) son: cualquier variación de Factory (simple, single, static, etc.), Builder, Decorator, Template Method y Strategy.

    Me flipa la idea de probar State para la lógica del dominio pero EF me hace un poco de stopper, al final no resulta sencillo tener clases muy diferentes a como espera encontrarlas EF y no valoro meter una capa extra «Data Mapper» o similar para tener entidades EF por un lado y entidades de dominio por otro, si hago eso para un mete-saca lo mismo estoy cayendo en sobreingenería… es un tema a investigar.

    Y por cierto, tu blog es como un agujero negro, he terminado leyendo el tema de las fachadas estáticas (muy bueno) y el último de métodos estáticos (también muy bueno), me han resuelto un montón de dudas! :)

    Gracias por el aporte y un saludo.

Comentarios cerrados.