Patrón de diseño Command

Introducción

El patrón de diseño Command es uno de los más útiles en nuestro catálogo de patrones para sistemas embebidos porque nos va a permitir desacoplar al sujeto que genera órdenes del sujeto que las debe llevar a cabo, y donde cada comando (u órden) es un objeto. (Recuerde que en los patrones de diseño todo son objetos, polimorfismo, clases base abstractas y concretas, interfaces, etc.)

Un ejemplo concreto de esto es un teclado de (digamos) tres interruptores. Cada vez que presionamos un interruptor mandamos una órden al sistema, y éste la ejecuta. Un escenario normal (sin usar a este patrón) es que las órdenes que ejecuta cada interruptor son fijas, y cada órden se codifica como función. El patrón Command permite que cambiemos en tiempo de ejecución la acción que cada interruptor debe llevar a cabo, así como la parte de nuestro sistema que ejecuta dicha órden o acción. En otras palabras, en un cierto estado la tecla A aumenta un contador, pero en otro estado la misma tecla A haría las veces de la tecla <Escape> o salir (o de cualquier otra cosa que queramos). El cambio de una funcionalidad a otra de la tecla A la llevamos a cabo en forma dinámica.

También se dice que este patrón es la versión orientada a objetos de una callback, y me parece que este es un buen punto de partida para entenderlo.

Para intentar explicar a este patrón voy a comenzar al revés de lo que normalmente se encontraría uno en la literatura u otros tutoriales: empecemos por nuestro cliente, la función main() y utilizaremos los mismos nombres comunes que se usan para este patrón. (Recuerde que el ‘cliente’ de Z es aquella función, clase o método que declara objetos variables del tipo Z. Y como tampoco soy un genio de los patrones, al final dejaré algunos enlaces en los que me basé para este tutorial):

// Driver program (o nuestro 'cliente')
int main()
{
    Receptor r;
    // r es quien va a ejecutar los comandos (ú ordenes). Por favor noten que r
    // no conoce a su invocador.
    
    ComandoA cmdA (&r);
    // comando A
    // Aquí podemos ver que vinculamos al comando cmdA con el receptor r.
    
    Invocador inv (&cmdA);
    // inv es el sujeto que invoca al comando cmdA. En este punto inv no sabe
    // que el receptor r es quien ejecutará su órden.
    
    inv.Invocar();
    // inv envía el mensaje Invocar(), que a su vez ejecuta el comando cmdA a
    // través del sujeto r.
    // Y he aquí la magia: el sujeto inv ejecuta el comando cmdA sin saber que
    // será el sujeto r el que lo lleve a cabo; y a su vez, el sujeto r no sabrá
    // que el comando cmdA proviene de inv.
}

Veamos los participantes en este patrón:

  1. Cliente. Como ya se mencionó es la parte del código que hace uso de este patrón. En nuestro ejemplo es la función main(), pero también podría ser una clase o un método.
  2. ComandoA. Es el comando que queremos ejecutar. Los comandos están en forma de objeto. En general, cmdA es una instancia de la clase ComandoA que a su vez es una clase heredada (o mejor dicho, clase concreta) de la clase base abstracta Comando (que aún no está representada en nuestro código, pero pronto lo estará).
  3. Invocador. Es el sujeto que quiere ejecutar al comando (o a uno de varios comandos). En un sistema embebido éste sería una tecla, por ejemplo.
  4. Receptor. Es la parte del código que recibe al comando y lo ejecuta.

Estudiando al código anterior podemos deducir varias cosas importantes del patrón Command.

  • Pueden haber tantos receptores (o ejecutores) de comandos como necesitemos.
  • Pueden haber tantos comandos como necesitemos.
  • Pueden haber tantos invocadores como necesitemos.
  • Y lo más interesante de todo: pueden haber tantas combinaciones de lo anterior como sea necesario.

Implementación

Para llevar a cabo la implementación de este patrón lo primero que necesitamos, es por supuesto, los comandos.

Clase base abstracta Comando

Todos los comandos deberán ser instancias de clases que hereden de Comando.

class Comando
{
public:
    virtual void Exec () = 0;
};

 

Comandos u órdenes

Cada comando deberá ser una instancia de alguna de las clases que hereden de la clase base abstracta Comando. En este ejemplo en particular sólo tenemos un comando. La declaración adelantada se necesita porque vamos a hacer referencia a ella dentro de las clases heredadas.

class ComandoA : public Comando
{
private:
    Receptor * r;

public:
    ComandoA (Receptor * _r) : r (_r) { /* nada */ }
    // vinculamos al comando con el receptor

    void SetReceptor (Receptor * _r) { r = _r; }
    // por si más adelante deseamos cambiar al receptor de este comando
    
    void Exec() override { r->Accion (); }
};

Clase Receptor

class Receptor
{
public:
    void Accion () { std::cout << "Comando recibido\n"; }
    // cada vez que el receptor es invocado se ejecuta la 'Accion' asociada a
    // ese comando en este receptor
};

Clase Invocador

class Invocador
{
private:
    Comando * cmd;
    // cmd vincula un comando con una instancia de Invocador

public:
    Invocador (Comando * _cmd) : cmd (_cmd) { /* nada */ }

    void SetComando (Comando * _cmd) { cmd = _cmd; }
    // por si más adelante el invocador desea cambiar de comando

    void Invocar () { cmd->Exec (); }
};

Código completo

Aquí tenemos el código completo de este ejemplo. Es indispensable que el lector tome en cuenta (porque siempre pasa en el diseño con patrones) el órden en que se implementan las clases, porque podría darse el caso de que el código no compile debido a que no encuentre la declaración de una clase utilizada por otra clase, pero que está declarada más adelante. A veces esto se soluciona utilizando declaraciones adelantadas. En nuestro ejemplo no hemos tenido esos inconvenientes, pero aún así el lector debe estar conciente de ello.

#include <iostream>

class Comando
{
public:
    virtual void Exec () = 0;
};

class Receptor
{
public:
    void Accion () { std::cout << "Comando recibido\n"; }
    // cada vez que el receptor es invocado se ejecuta la 'Accion' asociada a
    // ese comando en este receptor
};

class ComandoA : public Comando
{
private:
    Receptor * r;

public:
    ComandoA (Receptor * _r) : r (_r) { /* nada */ }
    // el comando toma a su receptor

    void SetReceptor (Receptor * _r) { r = _r; }
    // por si más adelante deseamos cambiar al receptor de este comando
    
    void Exec () override 
    { 
        r->Accion (); 
    }
    
};


class Invocador
{
private:
    Comando * cmd;
    // cmd vincula un comando con una instancia de Invocador

public:
    Invocador (Comando * _cmd) : cmd (_cmd) { /* nada */ }

    void SetComando (Comando * _cmd) { cmd = _cmd; }
    // por si más adelante el invocador desea cambiar de comando

    void Invocar () { cmd->Exec (); }
};

/*
 * Driver program (o nuestro 'cliente')
 */
int main()
{
    Receptor r;
    // r es quien va a ejecutar los comandos (ú ordenes). Por favor noten que r
    // no conoce a su invocador.
    
    ComandoA cmdA (&r);
    // comando A
    // Aquí podemos ver que vinculamos al comando cmdA con el receptor r.
    
    Invocador inv (&cmdA);
    // inv es el sujeto que invoca al comando cmdA. En este punto inv no sabe
    // que el receptor r es quien ejecutará su órden.
    
    inv.Invocar();
    // inv envía el mensaje Invocar(), que a su vez ejecuta el comando cmdA a
    // través del sujeto r.
    // Y he aquí la magia: el sujeto inv ejecuta el comando cmdA sin saber que
    // será el sujeto r el que lo lleve a cabo; y a su vez, el sujeto r no sabrá
    // que el comando cmdA proviene de inv.
}

Conclusiones

En primera instancia los patrones de diseño parecen (y realmente son) demasiado abstractos. Pero con un poco de tiempo y algún ejemplo que tenga que ver con nuestro trabajo se hace más fácil tanto entenderlos como implementarlos. Lo que sí es indispensable es que el lector tenga sólidos conocimientos tanto de diseño orientado a objetos (abstracción, herencia, polimorfismo, clases abstractas), como del lenguaje en que se implementará el patrón, en nuestro caso, C++.

En una siguiente entrega tendré un ejemplo más concreto (que tenga que ver con sistemas embebidos) para que el lector pueda afianzar sus conocimientos y aplicar el patrón a sus futuros diseños (o reescribir sus diseños actuales).

También, si el lector gusta de los diagramas UML esta entrada de la Wikipedia podría serle útil. (Muchas veces los diagramas UML de los patrones, y la definición formal de los mismos, obscurecen su sencillez, flexibilidad y elegancia.)

 

Fuentes

http://arsenmk.blogspot.mx/2012/07/command-pattern-implementation.html

https://sourcemaking.com/design_patterns/command/cpp/2

https://en.wikibooks.org/wiki/C%2B%2B_Programming/Code/Design_Patterns/Behavioral_Patterns

Y por si a alguien le interesa, aquí dejo una parte de mis anotaciones personales, que básicamente es la transcripción de este tutorial para mi entendimiento personal.

2015-12-14 18.32.51-1

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s