Mi granito de java: Command

martes, 7 de junio de 2011

Command

Encapsula un mensaje como un objeto. Especifica una forma simple de separar la ejecución de un comando, del entorno que generó dicho comando. Permite solicitar una operación a un objeto sin conocer el contenido ni el receptor real de la misma. Si bien estas definiciones parecen un tanto ambigüas, sería oportuno volver a leerlas luego de entender el ejemplo.

Este patrón suele establecer en escenarios donde se necesite encapsular una petición dentro de un objeto, permitiendo parametrizar a los clientes con distintas peticiones, encolarlas, guardarlas en un registro de sucesos o implementar un mecanismo de deshacer/repetir.

Se debe usar cuando:
  • Se necesiten colas o registros de mensajes.
  • Se deba tener la posibilidad de deshacer las operaciones realizadas.
  • Se necesite uniformidad al invocar las acciones.
  • Se quiera facilitar la parametrización de las acciones a realizar.
  • Se quiera independizar el momento de petición del de ejecución.
  • El parámetro de una orden puede ser otra orden a ejecutar.
  • Se busque desarrollar sistemas utilizando órdenes de alto nivel que se construyen con operaciones sencillas (primitivas).
  • Se necesite sencillez al extender el sistema con nuevas acciones.
Lo que permite el patrón Command es desacoplar al objeto que invoca a una operación de aquél que tiene el conocimiento necesario para realizarla. Esto nos otorga muchísima flexibilidad: podemos hacer, por ejemplo, que una aplicación ejecute tanto un elemento de menú como un botón para hacer una determinada acción. Además, podemos cambiar dinámicamente los objetos Command.

Diagrama UML


Command: declara una interfaz para ejecutar una operación.
CommandConcreto: define un enlace entre un objeto “Receiver” y una acción. Implementa el método execute invocando la(s) correspondiente(s) operación(es) del “Receiver”.
Cliente: crea un objeto “CommandConcreto” y establece su receptor.
Invoker: le pide a la orden que ejecute la petición.
Receiver: sabe como llevar a cabo las operaciones asociadas a una petición. Cualquier clase puede hacer actuar como receptor.

El cliente crea un objeto “CommandConcreto” y especifica su receptor. Un objeto “Invoker” almacena el objeto “CommandConcreto”. El invocador envía una petición llamando al método execute sobre la orden.
El objeto “CommandConcreto”, invoca operaciones de su receptor para llevar a cabo la petición.


Ejemplo

Escenario: una empresa maneja varios servidores y cada uno de ellos deben correr diversos procesos, como apagarse, prenderse, etc. Cada uno de estos procesos, a su vez, implican pequeños pasos como, por ejemplo, realizar una conexión a dicho servidor, guardar los datos en un log, etc.
Dado que cada servidor tiene su propia lógica para cada operación, se decidió crear una interfaz llamada IServer que deben implementar los servidores:




Obviamente en un caso real los métodos tendrían el algoritmo necesario para realizar tales operaciones. Hemos simplificado estos algoritmos y en su lugar realizamos una salida por consola en cada método.
Hasta aquí nada nuevo, sólo clases que implementan una interfaz y le dan inteligencia al método. Comenzaremos con el Command:



Ahora realizaremos un invocador, es decir, una clase que simplemente llame al método execute:


Veamos como funciona el ejemplo:



Consecuencias
  • Command desacoplado: el objeto que invoca la operación de aquél que sabe como realizarla.
  • Las órdenes son objetos manipulados y extendidos de forma natural.
  • Se pueden ensamblar órdenes en una orden compuesta.
  • Facilidad de adición de nuevos objetos Command.

3 comentarios:

Anónimo dijo...

podrias aclarar cual es el reciver, invoker y cliente en el ejemplo? aprobecho para felicitarte por tu blog. Me parece exelente.

Muchas Gracias

matias

Max dijo...

Muchas gracias por tus comentarios Matias! El reciever es el target, es decir el objeto que se llama en el execute del command. En el ejemplo serian los servidores. El invocador es el que llama al command. Es quizas el objeto menos util de este patron. Fijate que hice una clase con ese nombre y su instancia la llame serverAdmin...solo para cumplir con el patron pero podrias obviarla tranquilamente! Saludos!

Comandante_Shepard dijo...

Muchas gracias necesitaba esto!!!