En la programación orientada a objetos , el patrón de comando es un patrón de diseño de comportamiento en el que se utiliza un objeto para encapsular toda la información necesaria para realizar una acción o activar un evento en un momento posterior. Esta información incluye el nombre del método, el objeto que posee el método y los valores de los parámetros del método.
Cuatro términos siempre asociados con el patrón de comando son command , receiver , vocationr y client . Un objeto command conoce receiver e invoca un método del receptor. Los valores para los parámetros del método receiver se almacenan en el command . El objeto receiver para ejecutar estos métodos también se almacena en el objeto command por agrupación . El receptor luego hace el trabajo cuando se llama al execute()método en command . Un objeto invocador sabe cómo ejecutar un comando y, opcionalmente, lleva la contabilidad sobre la ejecución del comando. El invocador no sabe nada sobre un comando concreto, solo sabe sobre la interface del comando . Los objetos invocadores, los objetos de comando y los objetos receptores están en poder de un objeto cliente . El cliente decide qué objetos receptores asigna a los objetos de comando y qué comandos asigna al invocador. El cliente decide qué comandos ejecutar en qué puntos. Para ejecutar un comando, pasa el objeto de comando al objeto invocador.
El uso de objetos de comando facilita la construcción de componentes generales que necesitan delegar, secuenciar o ejecutar llamadas a métodos en el momento que elijan sin necesidad de conocer la clase del método o los parámetros del método. El uso de un objeto invocador permite llevar un registro de las ejecuciones de comandos de forma cómoda, así como implementar diferentes modos para los comandos, que son administrados por el objeto invocador, sin necesidad de que el cliente esté al tanto de la existencia del registro o de los modos.
El patrón de diseño de comando [1]
es uno de los veintitrés patrones de diseño GoF bien conocidos que describen cómo resolver problemas de diseño recurrentes para diseñar software orientado a objetos flexible y reutilizable, es decir, objetos que son más fáciles de implementar, cambiar, probar y reutilizar.
El uso del patrón de diseño de comandos puede resolver estos problemas: [2]
Se debe evitar vincular el invocador de una solicitud a una solicitud en particular. Es decir, se deben evitar las solicitudes cableadas.
Debería ser posible configurar un objeto (que invoca una solicitud) con una solicitud.
Implementar (cablear) una solicitud directamente en una clase es inflexible porque acopla la clase a una solicitud particular en tiempo de compilación, lo que hace imposible especificar una solicitud en tiempo de ejecución.
El uso del patrón de diseño de comando describe la siguiente solución:
Defina objetos separados (comando) que encapsulen una solicitud.
Una clase delega una solicitud a un objeto de comando en lugar de implementar una solicitud particular directamente.
Esto permite configurar una clase con un objeto de comando que se utiliza para ejecutar una solicitud. La clase ya no está asociada a una solicitud en particular y no tiene conocimiento (es independiente) de cómo se ejecuta la solicitud.
Vea también el diagrama de clases y secuencias UML a continuación.
Estructura
Diagrama de clases y secuencias UML
En el diagrama de clases UML anterior , la clase no implementa una solicitud directamente. En cambio, hace referencia a la interfaz para realizar una solicitud ( ), lo que la hace independiente de cómo se realiza la solicitud. La clase implementa la interfaz realizando una acción en un receptor ( ).InvokerInvokerCommandcommand.execute()InvokerCommand1Commandreceiver1.action1()
El diagrama de secuencia UML
muestra las interacciones en tiempo de ejecución: El objeto llama a un objeto. llama a un objeto, que realiza la solicitud.Invokerexecute()Command1Command1action1()Receiver1
Diagrama de clases UML
Usos
Botones y elementos de menú de la GUI
En la programación Swing y Borland Delphi , un objeto Actiones un objeto de comando. Además de la capacidad de ejecutar el comando deseado, una acción puede tener un icono asociado, un atajo de teclado, un texto de información sobre herramientas, etc. Un botón de la barra de herramientas o un componente de elemento de menú se pueden inicializar completamente utilizando únicamente el objeto Acción .
Si todas las acciones del usuario están representadas por objetos de comando, un programa puede registrar una secuencia de acciones simplemente manteniendo una lista de los objetos de comando a medida que se ejecutan. Luego puede "reproducir" las mismas acciones ejecutando los mismos objetos de comando nuevamente en secuencia. Si el programa incorpora un motor de scripts, cada objeto de comando puede implementar un método toScript() y las acciones del usuario pueden entonces registrarse fácilmente como scripts.
Al utilizar lenguajes como Java, donde el código puede transmitirse o transferirse desde una ubicación a otra a través de URLClassloaders y Codebases, los comandos pueden permitir que se envíe un nuevo comportamiento a ubicaciones remotas (comando EJB, trabajador maestro).
Si todas las acciones del usuario en un programa se implementan como objetos de comando, el programa puede mantener una pila de los comandos ejecutados más recientemente. Cuando el usuario desea deshacer un comando, el programa simplemente extrae el objeto de comando más reciente y ejecuta su método undo() .
Redes
Es posible enviar objetos de comando completos a través de la red para que se ejecuten en otras máquinas, por ejemplo, acciones de jugadores en juegos de computadora.
Procesamiento paralelo
Donde los comandos se escriben como tareas en un recurso compartido y son ejecutados por muchos subprocesos en paralelo (posiblemente en máquinas remotas; esta variante a menudo se conoce como el patrón Maestro/Trabajador)
Supongamos que un programa tiene una secuencia de comandos que ejecuta en orden. Si cada objeto de comando tiene un método getEstimatedDuration() , el programa puede estimar fácilmente la duración total. Puede mostrar una barra de progreso que refleje de manera significativa lo cerca que está el programa de completar todas las tareas.
Una clase de grupo de subprocesos típica y de propósito general podría tener un método addTask() público que agrega un elemento de trabajo a una cola interna de tareas que esperan ser realizadas. Mantiene un grupo de subprocesos que ejecutan comandos desde la cola. Los elementos de la cola son objetos de comando. Por lo general, estos objetos implementan una interfaz común como java.lang.Runnable que permite que el grupo de subprocesos ejecute el comando a pesar de que la clase del grupo de subprocesos en sí se escribió sin ningún conocimiento de las tareas específicas para las que se usaría.
De manera similar a la operación de deshacer, un motor de base de datos o un instalador de software pueden mantener una lista de operaciones que se han realizado o se realizarán. Si una de ellas falla, todas las demás se pueden revertir o descartar (lo que se suele denominar rollback ). Por ejemplo, si se deben actualizar dos tablas de base de datos que se refieren entre sí y la segunda actualización falla, la transacción se puede revertir, de modo que la primera tabla no contenga una referencia no válida.
A menudo, un asistente presenta varias páginas de configuración para una única acción que se lleva a cabo únicamente cuando el usuario hace clic en el botón "Finalizar" en la última página. En estos casos, una forma natural de separar el código de la interfaz de usuario del código de la aplicación es implementar el asistente mediante un objeto de comando. El objeto de comando se crea cuando se muestra el asistente por primera vez. Cada página del asistente almacena sus cambios en la interfaz gráfica de usuario en el objeto de comando, por lo que el objeto se completa a medida que el usuario avanza. "Finalizar" simplemente activa una llamada aexecute() . De esta manera, la clase de comando funcionará.
Terminología
La terminología utilizada para describir las implementaciones de patrones de comandos no es uniforme y, por lo tanto, puede resultar confusa. Esto es el resultado de la ambigüedad , el uso de sinónimos y las implementaciones que pueden oscurecer el patrón original al ir mucho más allá de él.
Ambigüedad.
El término comando es ambiguo. Por ejemplo, mover hacia arriba, mover hacia arriba puede referirse a un solo comando (mover hacia arriba) que debe ejecutarse dos veces, o puede referirse a dos comandos, cada uno de los cuales hace lo mismo (mover hacia arriba). Si el primer comando se agrega dos veces a una pila de deshacer, ambos elementos en la pila hacen referencia a la misma instancia de comando. Esto puede ser apropiado cuando un comando siempre se puede deshacer de la misma manera (por ejemplo, mover hacia abajo). Tanto la Banda de los Cuatro como el ejemplo de Java a continuación utilizan esta interpretación del término comando . Por otro lado, si los últimos comandos se agregan a una pila de deshacer, la pila hace referencia a dos objetos separados. Esto puede ser apropiado cuando cada objeto en la pila debe contener información que permita deshacer el comando. Por ejemplo, para deshacer un comando de selección de eliminación , el objeto puede contener una copia del texto eliminado para que pueda volver a insertarse, si el comando de selección de eliminación debe deshacerse. Tenga en cuenta que el uso de un objeto separado para cada invocación de un comando también es un ejemplo del patrón de cadena de responsabilidad .
El término "ejecutar" también es ambiguo. Puede referirse a la ejecución del código identificado por el método de ejecución del objeto de comando. Sin embargo, en Windows Presentation Foundation de Microsoft se considera que un comando se ha ejecutado cuando se ha invocado el método de ejecución del comando , pero eso no significa necesariamente que se haya ejecutado el código de la aplicación. Eso ocurre solo después de un procesamiento de eventos adicional.
Cliente, Fuente, Invocador : el botón, el botón de la barra de herramientas o el elemento del menú en el que se hizo clic, la tecla de acceso directo presionada por el usuario.
Objeto de comando, objeto de comando enrutado, objeto de acción : un objeto singleton (por ejemplo, solo hay un CopyCommandobjeto), que conoce las teclas de acceso directo, las imágenes de los botones, el texto del comando, etc., relacionados con el comando. Un objeto de origen o invocador llama al método de ejecución o ejecución del objeto de comando o acción. El objeto de comando/acción notifica a los objetos de origen/invocador apropiados cuando la disponibilidad de un comando/acción ha cambiado. Esto permite que los botones y los elementos del menú se vuelvan inactivos (atenuados) cuando no se puede ejecutar/realizar un comando/acción.
Receptor, objeto de destino : el objeto que se va a copiar, pegar, mover, etc. El objeto receptor es el propietario del método que se llama mediante el método de ejecución del comando . El receptor suele ser también el objeto de destino. Por ejemplo, si el objeto receptor es un cursor y se llama al método moveUp, se esperaría que el cursor sea el objetivo de la moveUpacción. Por otro lado, si el código está definido por el propio objeto de comando, el objeto de destino será un objeto completamente diferente.
Objeto de comando, argumentos de evento enrutados, objeto de evento : el objeto que se pasa desde la fuente al objeto de comando/acción, al objeto de destino y al código que realiza el trabajo. Cada clic de botón o tecla de acceso directo genera un nuevo objeto de comando/evento. Algunas implementaciones agregan más información al objeto de comando/evento a medida que se pasa de un objeto (por ejemplo CopyCommand, ) a otro (por ejemplo, sección de documento). Otras implementaciones colocan objetos de comando/evento en otros objetos de evento (como un cuadro dentro de un cuadro más grande) a medida que se mueven a lo largo de la línea, para evitar conflictos de nombres. (Véase también patrón de cadena de responsabilidad ).
Controlador ExecutedRoutedEventHandler, método, función : el código real que realiza las operaciones de copiar, pegar, mover, etc. En algunas implementaciones, el código del controlador es parte del objeto de comando/acción. En otras implementaciones, el código es parte del objeto receptor/destino y, en otras implementaciones, el código del controlador se mantiene separado de los demás objetos.
Administrador de comandos, administrador de deshacer, programador, cola, despachador, invocador : un objeto que coloca objetos de comando/evento en una pila de deshacer o de rehacer, o que retiene objetos de comando/evento hasta que otros objetos estén listos para actuar sobre ellos, o que enruta los objetos de comando/evento al objeto receptor/destino apropiado o al código del controlador.
Implementaciones que van mucho más allá del patrón de comando original.
Windows Presentation Foundation (WPF) de Microsoft introduce comandos enrutados, que combinan el patrón de comandos con el procesamiento de eventos. Como resultado, el objeto de comando ya no contiene una referencia al objeto de destino ni una referencia al código de la aplicación. En su lugar, al invocar el comando de ejecución del objeto de comando se genera un denominado Evento enrutado ejecutado que, durante la tunelización o propagación del evento, puede encontrar un denominado objeto de enlace que identifica el destino y el código de la aplicación, que se ejecuta en ese punto.
Ejemplo
Esta implementación de C++14 se basa en la implementación anterior a C++98 del libro.
#include <iostream> #include <memoria>clase Command { public : // declara una interfaz para ejecutar una operación. virtual voidexecute ( ) = 0 ; virtual ~ Command () = default ; protected : Command () = default ; };plantilla < typename Receptor > clase SimpleCommand : public Command { // ComandoConcreto public : typedef void ( Receptor ::* Acción )(); // define un enlace entre un objeto Receptor y una acción. SimpleCommand ( std :: shared_ptr < Receptor > receptor_ , Acción acción_ ) : receptor ( receptor_ . get ()), acción ( acción_ ) { } SimpleCommand ( const SimpleCommand & ) = delete ; // regla de tres const SimpleCommand & operator = ( const SimpleCommand & ) = delete ; // implementa la ejecución invocando la(s) operación(es) correspondiente(s) en Receptor. virtual void ejecutar () { ( receptor ->* acción )(); } privado : Receptor * receptor ; Acción acción ; };class MyClass { // Receptor public : // sabe cómo realizar las operaciones asociadas con la ejecución de una solicitud. Cualquier clase puede servir como Receptor. void action () { std :: cout << "MyClass::action \n " ; } };int main () { // Los punteros inteligentes evitan fugas de memoria. std :: shared_ptr < MyClass > receiver = std :: make_shared < MyClass > (); // ... std :: unique_ptr < Command > command = std :: make_unique < SimpleCommand < MyClass > > ( receiver , & MyClass :: action ); // ... command -> execute (); }
La primera mención publicada del uso de una clase Command para implementar sistemas interactivos parece ser un artículo de 1985 de Henry Lieberman. [4] La primera descripción publicada de un mecanismo de deshacer-rehacer (de múltiples niveles), utilizando una clase Command con métodos de ejecución y deshacer , y una lista de historial, parece ser la primera edición (1988) del libro de Bertrand Meyer Object-oriented Software Construction , [5] sección 12.2.
Referencias
^ Erich Gamma; Richard Helm; Ralph Johnson; John Vlissides (1994). Patrones de diseño: elementos de software reutilizable orientado a objetos . Addison Wesley. pp. 233ff. ISBN0-201-63361-2.
^ "El patrón de diseño Command: problema, solución y aplicabilidad". w3sDesign.com . Archivado desde el original el 23 de septiembre de 2020. Consultado el 12 de agosto de 2017 .
^ "El patrón de diseño Command - Estructura y colaboración". w3sDesign.com . Consultado el 12 de agosto de 2017 .[ enlace muerto ]
^ Lieberman, Henry (1985). "Los sistemas de menús son mucho más de lo que se ve en la pantalla". ACM SIGGRAPH Computer Graphics . 19 (3): 181–189. doi :10.1145/325165.325235.
^ Meyer, Bertrand (1988). Construcción de software orientado a objetos (1.ª ed.). Prentice-Hall.
Enlaces externos
Wikimedia Commons tiene medios relacionados con Patrón de comando .
El Wikilibro Patrones de diseño en informática tiene una página sobre el tema: Implementaciones de comandos en varios lenguajes
Patrón de comando
Consejo Java n.° 68: aprenda a implementar el patrón Command en Java