Introducción
El patrón Command (command pattern) es un patrón de diseño en el que los objetos representan acciones que serán consumidas por algún consumidor.
Problema
El patrón Command se utiliza para resolver diversos tipos de problemas en desarrollo de software:
- Usado como simplificación de llamadas. En ocasiones el número de parámetros de una función crece hasta hacerse inmanejable. El uso del patrón command permite agrupar los parámetros en un solo objeto que se pasará a la función.
- Usado como cola de comandos. Encapsular cada comando en un objeto permite su procesamiento de forma secuencial en forma de cola, de forma que un proceso trabajador pueda ir sacando los trabajos de dicha cola.
- Operaciones con histórico. Usando objetos como comando y manteniendo esos comandos podemos mantener un histórico de los comandos utilizados y, en caso necesario, revertirlos.
- Generalización de comandos. En ocasiones el uso de comandos permite la abstracción de dicho comando de forma que el comando constituye una clase abstracta de la que heredan los comandos específicos y que permite que un determinado objeto procese comandos ajustados a un determinado esquema independientemente de lo que haga dicho parámetro.
Código de ejemplo
public
procedure Execute; virtual; abstract;
end;
type CUpdateTextCommand = class(CCommand)
private
m_text : string;
public
procedure Execute; override;
constructor Create(text : string);
end;
type CCallExtLibrary = class(CCommand)
public
procedure Execute; override;
end;
CUpdateTextCommand.Execute;
begin
MainForm.TextBox = m_text;
end;
CCallExtLibrary.Execute;
begin
ExtLibrary.DoYourThing(1,'Parametro');
end;
procedure Thread1.ActualizarTextoTextBox(texto : string);
begin
MainThread.ColaMensajes.Push(CUpdateTextCommand.Create(texto);
end;
procedure Thread1.LlamarLibreríaExterna;
begin
MainThread.ColaMensajes.Push(CCallExtLibrary.Create);
end;
De esta forma encapsulamos cada uno de los parametros y el código del hilo principal podría ser
begin
CCommand msg := ColaMensajes.Pop;
msg.Execute;
end;
Ejemplos reales
Trabajos de impresión
Un ejemplo muy común es su uso en trabajos de impresión encapsulando toda la información necesaria para la impresión, como por ejemplo el numero de copias, la impresora en la que imprimir, la información del tamaño de página, si se debe imprimir en blanco y negro o color, etc. Toda esa información, incluyendo quizá incluso la representación binaria de los datos a imprimir, puede pasarse al proceso de impresión o, más probablemente, introducirse en una cola de impresión de la cual una tarea irá leyendo los trabajos a imprimir.
Programa de dibujo
En los programas de dibujo es casi obligatoria la presencia de una opción para deshacer lo hecho, paso a paso, en previsión de posibles meteduras de pata. Aunque hay múltiples formas de implementarlo, una de ellas consiste en encapsular cada acción dentro de un objeto, de forma que el programa sea capaz de ir recorriendo dichos objetos en orden inverso e ir deshaciendo las operaciones llevadas a cabo.
Algunas operaciones pueden deshacerse aplicando la acción inversa mientras que otras deberán contener (en este caso) los datos necesarios para devolver la imagen a su estado anterior (por ejemplo los bits que han cambiado desde un estado al anterior y como).
Procesador de ordenes por el hilo principal
En determinados lenguajes (por ejemplo Delphi) o debido al diseño de algunas librerías de terceros existen algunas limitaciones en la metodología multihilo, de forma que algunas llamadas es necesario que sean realizadas por el hilo principal de la aplicación. En estos casos el patrón Command nos permite encapsular cualquier tipo de comando que queramos realizar y apilarlo en una cola de órdenes para procesar por parte del hilo principal cuando tenga tiempo (ver el ejemplo de código).
Simplificando el API
Aunque esto no es un ejemplo de uso real específico podemos observar las diferencias entre un API simplificada con un patrón command y un API que no lo está, así como la mayor legibilidad del primero.
| public void Print(int printHandle, bool blancoYNegro, TTamPagina pag, int numCopias, int pagInicio, int pagFin, byte[] data); plublic void ImprimeArchivo(string file) { Print(Printers.Default, true, TTamPagina.A3, 1,1,23, NombreABytes(file)); } |
public void Print(CPrintJob Trabajo); public void ImprimerArchivo(strin file) { CPrintJob j = new DefaultJob(); j.data = NombreABytes(file); j.tamPag = TTamPagina.A3; Print(j); } |
Se observa a simple vista que el segundo código es más fácil de leer y entender. El uso de una estructura (clase) para mantener la información nos da la posibilidad de simplificar el paso de parámetros por defecto y hace más evidente qué estamos pasando al mostrar de forma directa los nombres de los parámetros.
Comentarios recientes
hace 13 semanas 6 días
hace 23 semanas 4 días
hace 45 semanas 2 días
hace 45 semanas 2 días