El patrón Factory

El patrón Factory

El patrón Factory consiste ni más ni menos que en delegar la creación de objetos en un componente aislado.

Puede que no te diga mucho la frase anterior, pero eso es porque no has visto todavía las ventajas de una constante en todos los patrones de diseño: el desacoplamamiento entre componentes. Por ejemplo, imaginemos que una clase llamada Empresa necesita devolver un objeto en función de un input del usuario por teclado.

La clase Main y la clase Electrodoméstico están altamente acopladas. ¿Por qué? Porque un cambio en los electrodomésticos (la desaparición de un producto, la inclusión de otro) obliga a modificar el código fuente de la clase Main. Por eso, el primer paso para hacer un buen código es separar lo que varía de lo que permanece constante, y de esta forma generar espacios de código donde el mantenimiento del programa esté más controlado.

Veamos la representación del anterior snippet de código en un diagramas UML donde los componentes están altamente acoplados.

Vemos que la clase Main depende de tres clases (y estas clases pueden variar con el tiempo) por lo que tenemos el problema de que nuestro núcleo de la aplicación, el componente Main, va a ser cada vez más complicado de escalar a medida que avanza la vida de la aplicación. Es urgente separar el componente principal de esta funcionalidad ¿pero como lo hacemos? Creando otro componente.

Volviendo al código anterior: ¿que funcionalidades vemos en el programa? Principalmente dos: la entrada y salida al usuario de los datos y la creación de los objetos.

Por supuesto, podemos separar casi tanto como queramos las funciones en funciones más concretas, pero esta entrada no pretende tratar sobre arquitectura de software sino sobre el patrón factory, que consiste ni más ni menos que en crear un componente cuya función se la de la instanciar objetos.

Ahora tenemos una clase llamada ElectroCreator que se encarga de instanciar las clases concretas en función de un parámetro que le pasa la clase Main. Main ya no tiene que saber cómo crear el objeto, no es su función, sólo sabe que puede llamar al método de un componente que sí le devuelve un objeto. Y cómo lo haga le da igual mientras funcione.

Crear un componente por cada funcionalidad de la aplicación es en programación orientada objetos el abc de cualquier proyecto. Nos da muchísima más flexibilidad a la hora de mantener y testear, dado que está altamente aislado de otros componentes con los que interactúa. En desarrollo esto se traduce en una mayor independencia entre equipos y por tanto una mayor productividad. ¡Pero de alguna forma tienen que comunicarse los componentes entre sí! Si estuvieran completamente aislados no podrían hacer nada en conjunto. Efectivamente, y esto se hace gracias a elementos que abstraen el componente de su funcionalidad concreta: las interfaces.

Las interfaces nos permiten decirle a un objeto: «puedes ejecutar los siguientes métodos sobre esta otra serie de objetos, y no sabrás cómo lo hacen, pero funcionarán como esperas que funcionen y/o te retornarán el valor que esperas que retornen». ¡No más código funcionalmente distinto en la misma clase! ¡Un componente, una función!

Especializándonos en el patrón Factory

Si analizamos más detenidamente el ultimo snippet con lo que acabamos de decir,  nos damos cuenta de que podemos mejorarlo.

Imaginemos que podemos producir varios de tipos de microondas, lavadoras y neveras. Cada uno de estos tipos tendrán un consumo determinado y unas características determinadas.

Lo primero que podríamos pensar como desarrolladores es «no pasa nada, cada objeto puede tener esos atributos y ya les daremos el valor cuando creemos la instancia concreta. ¡Error! Hemos creado el patrón Factory para desacoplar precisamente la creación de los objetos. Queremos que Main tenga en su poder, simplemente llamando al creador concreto, el objeto que necesita.

Por ejemplo, podemos crear dos tipos de electrodomésticos, de gama básica y premium con valores distintos en sus atributos, y podemos hacerlo de dos formas gracias al patrón Factory. Porque en realidad existen dos tipos de patrón Factory.

Patrón Method Factory

El patrón Method Factory consiste en crear una clase por cada objeto que se pueda crear. Por ejemplo, en nuestro caso tendríamos una clase ElectrodomesticoFactory abstracta tantas subclases como productos vendamos, y cada una de ellas crearían el producto de una determinada forma.

Ahora en el Main solo tendremos que preocuparnos de obtener la referencia correcta del tipo de creador de electrodomésticos que queramos.

¿Que hemos conseguido creando la interfaz con el patrón Method Factory? Hemos desacoplado  aún más la clase con la que nos comunicamos desde el Main con las implementaciones concretas de los productos, de forma que si creamos nuevos tipos de productos, no tenemos que cambiar nuestra interfaz, solo crear una subclase de esta. Es decir, hemos cumplido con la máxima open-closed.

Patrón Abstract Factory

A diferencia del Method Factory, el patrón Abstract Factory crea una interfaz que agrupa varios métodos constructores pero para objetos diferentes. Veámoslo mejor con un diagrama:

Este patrón por tanto, crea una interfaz con métodos creadores de varios productos y los creadores concretos decidirán cómo se construyen y el tipo concreto a construir. Es lo que parece: una suerte de agrupación de clases concretas del patrón Method Factory.

Las diferencias fundamentales entre los dos patrones

Es normal confundirlos porque hacen algo muy parecido a primera vista y normalmente se pueden usar indistintamente, con dos grandes puntos a tener en cuenta:

  1. El patrón Abstract Factory podría requerir sobrescribir varias clases si se crea un elemento nuevo. En nuestro caso, si introducimos un lavavajillas, tendríamos que heredar createLavavajillas() en el creador concreto pero implicaría más o menos trabajo en función de nuestra aplicación y de cuántas formas de construir electrodomésticos (o subclases de la factoría abstracta) tengamos. Por el contrario, con el patrón Method Factory, solo tendríamos que crear una nueva clase que extendiera de la factoría abstracta.
  2. El patrón Method Factory tiene como contrapartida que si existen muchos elementos concretos que se pueden construir, y muchas formas de construirlos, es un método poco práctico de implementar*.
  3. El patrón Abstract Factory es muy útil para componer objetos. Pasando por ejemplo como argumento al constructor de un objeto una referencia a una factoría concreta, podemos obtener multitud de objetos en tiempo de ejecución, mientras que con el patrón Method Factory, esto sería mucho más engorroso, ya que deberíamos pasar una referencia al mismo constructor por cada objeto que quisiéramos crear.

*Aunque canónicamente se suele utilizar como ejemplo una subclase creadora concreta por producto, es posible (pagando un precio en manteniemiento) parametrizar el método creador. De esta forma podemos obtener varios electrodomésticos en un sólo creador concreto, que podría agrupar varios electrodomésticos según por ejemplo la calidad de su construcción. Sería una suerte de híbrido entre los dos patrones. Esto demuestra que los patrones no son una ley inmutable, sino meras herramientas con pros y contras que valorar.

El patrón Decorator

El patrón Decorator

El patrón Decorator se usa para componer dinámicamente una Clase. Si entiendes el patrón Strategy que explicamos aquí, no te será dificil entender el patrón Decorator.

Imagina que quieres «decorar» un objeto añadiendole multitud de objetos relacionados. Pongamos por ejemplo que queremos hacer una carta para un restaurante. Podríamos crear una clase llamada Comida con multitud de campos que señalasen la cantidad de cada ingrediente. Esta clase comida nos serviría para representar cualquier plato… a primera vista.

Podríamos sobrevivir con este esperpento  de código… la primera semana. En cuanto queramos escalar la aplicación para añadir nuevos ingredientes, nos encontraremos entrando y saliendo del código fuente, por no hablar de los cambios en el precio. Por supuesto podríamos componer esta clase con infinidad de objetos, pero afortunadamente existe un patrón que no solo te permite componer una comida con los ingredientes justos y necesarios, sino que encima te permite crear cualquier tipo de plato en tiempo de ejecución.

Se acabó el crear una clase para cada objeto. Ahora podemos tener una clase que se componga de elementos a medida que los vayamos necesitando, algo similar al patrón Strategy pero acumulativo.

¿Hasta ahora se parece mucho a un patron Strategy no? Pero nos falta poder hacer el componente Comida de los ingredientes acumulativo y no es muy complejo, pero hay que poner atención.

  1. Pasaremos la referencia de un ingrediente (que contendrá la comida acumulada) al nuevo ingrediente para actualizar su atributo Comida. De esta forma, cada ingrediente que creemos guardará su valor.
  2. Extenderemos los ingredientes para que hereden de Comida. Si no lo hacemos, los objetos que contienen la comida y que estamos pasando a otros constructores no podrían ser casteados al tipo del atributo.

Puede resultar bastante lioso en un principio, sobre todo si es la primera vez que lo ves, así que recapitulemos:

  1. Tenemos una clase Comida que queremos decorar.
  2. Creamos los decoradores, que son los ingredientes.
  3. Los ingredientes se crean obteniendo la referencia de una comida y guardándola en un atributo.
  4. Para acumular los ingredientes.
    1. Hacemos que extiendan Comida.
    2. Pasamos el antiguo ingrediente en el constructor del nuevo y guardamos su referencia.
    3. Repetimos el paso anterior hasta que queramos.

En un código muy sencillo (quitando cualquier tipo de abstracción) sería:

Fijaros en que para calcular los valores teniendo en cuenta las comidas acumuladas, se usa el atributo que hemos añadido para recuperar el valor, provocando un «efecto cascada».

Os recomiendo copiar el código de arriba y debuguearlo para comprender el flujo del programa y del patrón.

¿Cómo podríamos mejorar este código?

Si quisieramos crear implementaciones concretas de comida (comidas ya predefinidas) podríamos abstraer la clase comida y utilizar los ingredientes sobre estas clases, como muestra el siguiente UML más reconocible del patrón.

 

El patrón Observer en Swing

El patrón Observer en Swing

Esta entrada sirve para poner en práctica todo lo aprendido del patrón Observer en esta y esta entrada del blog. Si no sabéis qué es el patron Observer o teneis dudas, echadles un vistazo antes.

El patron observer en Swing es un poco más intrincado de lo que estudiamos en la teoría la primera vez, sobre todo por la gran cantidad de eventos y clases que hay, pero con un diagrama UML para una clase concreta, creo que podremos abstraer esta lógica.

Pongamos por ejemplo el caso de un JComboBox.

El JComboBox es un componente de Swing que nos permite seleccionar una opción de una lista desplegable, lo que nos da una pista: debe ser algo que se deba observar y este notificará cuando un evento como la selección de un ítem tenga lugar. Tendrá presumiblemente el rol de Observable.

Si echamos un vistazo a su implementación, nos encontraremos con que se compone entre otros, de un objeto de tipo EventListenerList. Si investigamos un poco, vemos que es un objeto muy sencillo , y la base del patrón Observer en Swing. No en vano JComponent (su clase padre) tiene este mismo campo, siendo según la documentación «The base class for both standard and custom components that use the Swing architecture».

Pongamos lo que sabemos por ahora en un diagrama UML: Un Componente de tipo JComboBox tiene un campo donde se almacenan diferentes objetos que extienden de la clase EventListener.

Click para ampliar

Ya tenemos al elemento Observable más o menos definido. Ahora falta definir el Observador, que puede ser cualquier cosa, siempre y cuando le podamos enviar mensajes.

Si recordamos, el observador debe implementar un método para recibir notificaciones. Ese método es el que se llama desde el elemento observado para desencadenar un algoritmo del que ya no es responsable.

¿Recuerdas de qué tipo son los oyentes en la lista de tipo EventListenerList del objeto Observable? Todos heredaban de una de tres interfaces: ActionListener, ItemListener y PopupMenuListener. Pues por definición, si queremos meter un oyente en esa lista para que sea notificado, debe implementar una de estas tres interfaces e implementar los métodos correspondientes.

El cómo llama al evento es algo que dejo para las personas que tengan paciencia para comprender a fondo como funciona Swing. Se ven implicadas otras clases y supondría desviarse del tema demasiado. Pero el cuándo, es fácil de adivinar: cuando se llaman a los métodos que comienzan por fire, por ejemplo fireActionEvent().

Así que si queremos que nuestra clase observadora sea notificada de un evento de un JComboBox solo tendremos que seguir estos dos pasos:

  1. Implementar la interfaz del evento que queramos escuchar y añadirle una funcionalidad.
  2. Añadir como oyente a nuestra clase observable de tipo JComboBox.

Click para ampliar

Y por último…

No hace falta decir que este modelo está muy simplificado, para facilitar la comparación con el modelo teórico. Muchas más interfaces heredan de EventListener y el JComboBox implementa varias interfaces para escuchar (es a la vez observadora y observada).

Os animo a bucear por el código fuente del JDK alguna vez en clases no muy enrevesadas. Es un gran ejercicio tanto para conocer el lenguaje como para coger fluidez en la lectura de código.

El patrón Observer en Java I

El patrón Observer en Java I

En la entrada del patrón observer, terminábamos con una pregunta.

¿Cómo podemos controlar cuándo se envían las notificaciones?

La respuesta es simple: añadiendo un estado al sujeto que envía las notificaciones mediante una variable. Si esta variable tiene un valor, notificamos a los observadores y la devolvemos a su estado original. En caso contrario no hacemos nada.

Por ejemplo, supongamos que la clase Balanza notifica a sus observadores cuando se ha puesto un peso sobre ella de más de 1000gr. Si no tuviéramos esa variable booleana en cada pesaje tendríamos que recorrer toda la lista, notificando de cualquier pesaje.

Podrías pensar acertadamente que existe cierta complicación en el código. ¿Por que no notificar directamente si se da una condición a los observadores? Bueno, en este ejemplo quizás si sería lo más lógico, pero sirve como excusa para extrapolar a otras situaciones donde las condiciones para notificar sean más complejas.

Aunque controlar las notificaciones no es algo contemplado en el propio patrón observer, la implementación de Java sí que añade por defecto esta posibilidad. Si echamos un vistazo al código de la clase Observable vemos que tiene dos campos: un Vector de objetos Observer y un atributo booleano.

Una herencia envenenada

Que Observable tenga una clase Vector para albergar los Observer puede ser indicativo de una necesaria huida. Pero aprovechemos que estamos aquí para hacer énfasis en el punto clave de esta implementación:

  • Observable es una clase concreta.

Esto implica que para hacer tu patrón Observer con la implementación de Java tienes una limitación: Tu objeto debe heredar de Observable, y si ya extiende otro objeto, no puedes hacer nada. ¿Que pasa si quieres customizar tu objeto Observable? Sigues teniendo que extender la clase. Y si quieres componer un objeto con otro que extienda Observable, olvídate de usar sus métodos para cambiar y leer el estado porque son protected.

Por supuesto esto tiene una explicación y es que estas dos clases, tal y como podéis ver en su documentación, existen desde el JDK 1.0. Por este y otros motivos se recomienda implementar tu propio patrón Observer a través de interfaces.

Puedes encontrar un ejemplo sencillo y comentado de la implementación del patrón observer en mi repositorio de Github.

 

El patrón Observer

El patrón Observer

El patrón Observer es posiblemente el más conocido de los patrones de diseño de software, quizás porque es el común denominador de cualquier aplicación gráfica.

Es muy fácil de explicar y se puede resumir en que cuando un objeto A le pasa algo, este se lo notifica a una serie de objetos llamados observadores. Quizás lo difícil sea implementarlo, pero vayamos por partes.

Según la definición tenemos dos tipos de agentes: un sujeto del cambio y un observador y por lo tanto tendremos dos clases: un Sujeto y un Observador.

Sabemos también que el sujeto debe notificar a un observador. Para ello haremos que el sujeto guarde en una lista a todos sus observadores.

¿Pero como lo notifica? Pues simplemente implementaremos un método en los observadores que sea llamado por el objeto observado mediante un bucle foreach.

Pero esto se puede mejorar…¿Como? El comportamiento que hemos visto es extensible para cualquier objeto sujeto a cambio y a sus observadores, por lo tanto, podemos abstraerlo en dos interfaces.

Esta generalización nos permite tener varios observadores u oyentes que hagan distintas cosas cuando el sujeto quiera notificar algo. Hemos desacoplando ambos objetos y ahora el sujeto ya no tiene que saber de qué tipo concreto es su oyente para notificarle un cambio.

¿Pero qué pasa con los observadores?

Como a lo mejor te habrás dado cuenta este diseño deja un poco desvalidas a las clases observadoras.  No controlan cuando deben dejar de serlo de una clase ni tienen control ninguno sobre cuándo quieren ser notificadas. No nos preocupemos, si queremos que las clases observadoras tengan el control de suscribirse y desuscribirse a eventos, solo necesitaremos hacer implementar una referencia a la clase que está observando para notificarle sus deseos.

¿Y si una clase solo quiere ser notificada en determinadas circunstancias? Bueno, aprovecharemos para explicar eso mientras vemos la implementación nativa de Java para este patrón en otra entrada.