El patrón Command es, en un lenguaje muy poco técnico,  la implementeación de un código que permite la creación de peticiones por parte del usuario, su almacenamiento y posterior ejecución sobre un receptor. Nos permite desacoplar el objeto que dispara una acción del objeto que la realiza.

Podemos en base a la «definición» anterior deducir que existen 4 entidades que interactúan entre sí.

  1. El cliente que realiza la petición.
  2. La propia petición.
  3. Un elemento intermediario entre el cliente y el receptor.
  4. El receptor que se encarga de ejecutar la petición.

El cliente es el sujeto que crea la petición en base a dos cuestiones: qué quiere hacer y sobre quién quiere hacerlo.

La petición es el elemento central del patrón. Contiene el código a ejecutar sobre el receptor y por ende, una referencia a este.

El elemento intermediario es el encargado de almacenar las peticiones y provocar su ejecución cuando el cliente lo necesite, o cuando se considere oportuno por cualquier otro motivo.

El receptor simplemente es el sujeto pasivo de la ejecución del comando disparado por el intermediario.

patron command uml

ODIO profundamente el diagrama UML «oficial» del patrón Command, porque creo que despista más que aclara a la hora de comprender por primera vez este patrón. ¿Como es posible que el intermediario no tenga ningún tipo de relación con el cliente y el receptor si? Es necesario intentar comprender por qué está así expuesto el diagrama UML.

El intermediario o invoker no tiene por que tener una relación directa con el cliente, como se ve en el diagrama. Puede o puede no tener relación, a fin de cuentas el cliente que exponemos aquí sólo tiene la obligación de crear el comando y vincularlo con su receptor, pero no de usar el invocador…¿no? El invocador puede estar manejado por un proceso independiente, otro cliente que puede llamar recursivamente cada cierto tiempo a la ejecución de sus comandos por ejemplo.

Nótese por tanto que el cliente que crea el comando puede no ser necesariamente el cliente que llame a su ejecución, aunque por regla general sea así.

En un diagrama de secuencias se aprecia mejor el funcionamiento del patrón, y cómo el invocador está desacoplado del receptor.

Veamos un ejemplo práctico:

Supongamos que nosotros (los clientes) queremos sacar dinero (petición) de un cajero automático (intermediario) de nuestra cuenta bancaria (receptor).

No sabemos cómo es la lógica interna del banco para darnos dinero, ni cómo funciona la máquina por dentro. Solo sabemos que necesitamos ejecutar la acción de sacar dinero sobre nuestra cuenta, así que le decimos al intermediario lo que queremos, generando un comando a medida.

El cajero recibe el comando, parametrizado con el dinero que queremos sacar y la cuenta sobre la que se ejecuta. En un momento determinado, llama a la ejecución de su método, que lleva implícito el código ejecutable que nosotros no conocemos.

El banco recibe las ordenes, haciendo los cambios oportunos en la cuenta, guardando un registro de la transacción, cobrando comisión si hubiera, etc.

Deshacer el último comando

¿Que pasa si el invocador he ejecutado un comando que no debería y necesitamos devolver el receptor de la acción a su estado previo? Esta es una de las características del patrón Command, que nos permite deshacer los cambios de la ultima llamada realizada.

Si todos los comandos tienen un método execute() que se encarga de realizar una operación, podemos obligar a esos mismos comandos a implementar un método definido en la interfaz que deshaga ese cambio. Por ejemplo si tenemos un comando que se encarga de cambiar el volumen de un altavoz, necesitariamos guardar el estado del volumen cada vez que llamemos al método execute() por si acto seguido queremos deshacer los cambios.

El problema de la infinidad de clases comando concretas

¿Te has dado cuenta de la cantidad de comandos concretos que necesitaríamos si quisiéramos crear un altavoz con 100 modos distintos de ejecutarse? Que se iluminase, que tuviera un regulador de volumen, de bajos, de agudos… Un comando para cada modo de cada característica de un solo aparato. ¡Acabaríamos teniendo una cantidad enorme de clases sin apenas código! Afortunadamente, a partir del JDK8, podemos usar las expresiones lambda, que nos permiten solucionar este problema definiendo la implementación del método abstracto que hereda una clase anónima de una interfaz funcional sin necesidad de crearla nosotros.