¿Cuánta lógica se puede poner en un comando? O de manera diferente: ¿para qué tipo de lógica es el patrón de comando?

8

He estado usando el patrón de comando durante bastante tiempo, pero nunca estoy seguro de cuánta lógica puedo poner realmente en el Executemétodo.

Mi implementación actual del patrón de comando es similar a esta:

public abstract class Command
{
    public static event EventHandler Completed = delegate { };        

    public bool Success { get; private set; }

    public Exception Exception {get; private set; }

    public abstract bool Execute();

    protected bool OnCompleted(bool success, Exception ex = null)
    {       
        Success = success;
        Exception = ex;
        Completed(this, EventArgs.Empty)
        return success;
    }
}

y estas son las preguntas que me hago (y practico en mis comandos):

  1. ¿Está bien mostrar cuadros de mensajes o cuadros de diálogo para abrir archivos, etc.?
  2. ¿Está bien establecer propiedades de cualquier objeto?
  3. ¿Puede un comando contener lógica empresarial?
  4. ¿Puede un comando modificar los controles de la GUI de todos modos?
  5. ¿A qué comandos de capa pertenecen? Vista o capa de datos? ¿Puedo tener comandos en ambas capas?
  6. ¿Puede un comando hacer todo lo que anteriormente estaba en el button1_Click?
  7. ¿Debería un comando por unidad comprobable?
  8. ¿Se puede ver un comando como un usuario que hace uso de las API y que crea la última capa de una aplicación y también el último recurso para detectar excepciones?
  9. ¿Pueden ejecutarse los comandos también por código (el comando llama a la API, la API se ejecuta y finalmente alguna otra API llama a un comando) o solo el usuario puede invocarlo y las API no deben saber sobre su existencia?
  10. ¿Hay un lugar para los comandos en MVC o MVVC o cualquier otro patrón de diseño con un controlador? Parecen ser mutuamente excluyentes. ¿Qué es preferible?

Hay muchos tutoriales que muestran cómo implementar el patrón de comando, pero ninguno realmente discute cómo aplicarlo en una aplicación real.

t3chb0t
fuente

Respuestas:

7

El patrón de comando se usa generalmente para desacoplar QUÉ de la OMS y QUÉ de CUÁNDO. Este es el beneficio de tener una interfaz simple tan simple como:

public abstract class Command {
    public abstract void execute();
}

Imaginemos que tienes una clase EngineOnCommand. Puede pasar este comando a otros objetos que acepten instancias de comando. Esto significa que la clase que recibe este EngineOnCommand, también podría recibir un comando diferente para ser ejecutado por él y nunca debería saberlo. Esto significa que desacopla QUÉ de la OMS .

Ahora, el segundo caso para el Patrón de Comando es desacoplar QUÉ de CUÁNDO . Imaginemos que desea crear un sistema en el que las acciones en la Base de datos solo se ejecuten de noche, pero siguiendo la secuencia en la que se solicitaron. Con una cola de Comandos en la que puede ejecutar uno por uno, podría haberlo logrado. La idea es que la invocación del comando en realidad solo desencadena una cola del comando en una lista para ejecutarse más tarde.

Espero que mis ejemplos anteriores ayuden a comprender cuál es la idea del patrón. Ahora intentaré responder algunas de sus preguntas utilizando lo anterior como base.

¿Puede un comando contener lógica empresarial?

Sí, creo que debería contenerlo. Lo contendrá de una manera desacoplada de la OMS y CUÁNDO.

¿Puede un comando modificar los controles de la GUI de todos modos?

Esto significa que lo está acoplando a la GUI. Esto significa que no se está utilizando con la intención de desacoplar QUÉ de la OMS. Lo ideal es que el comando no sepa quién lo invoca, si es GUI o una funcionalidad por lotes.

¿Debería un comando por unidad comprobable?

No debería, debe ser una unidad comprobable . Todo el código debe ser comprobable por unidad, pero idealmente todos los comandos deben ser comprobables por unidad.

Perdón por no responder todas sus preguntas, pero creo que debería consultar el libro GOF . Contiene algunos buenos ejemplos.

pietromenna
fuente
+1 Excelente respuesta, aunque un pequeño detalle: abogar por una práctica determinada, como las pruebas unitarias, es excelente, pero ordenar esas prácticas como si fueran universalmente obligatorias no suele ser útil para los programadores jóvenes. Hablando por experiencia.
Phil
2

La mayor parte del beneficio de los comandos es que facilitan deshacer una acción, rehacer una acción, realizar una acción en varios lugares (a través de una conexión de red) o realizarla en una fecha posterior, y así sucesivamente.

Con eso en mente:

  1. Probablemente no. Es difícil imaginar una situación en la que "deshacer" un cuadro de diálogo tenga sentido, y este es el tipo de cosas que preferiría poner en el código de la interfaz de usuario.

  2. Mientras ese objeto sea accesible desde cualquier lugar que desee ejecutar el comando, entonces, por supuesto, está bien cambiar sus propiedades.

  3. Absolutamente. Esto se relaciona con si su modelo debe "contener" la lógica empresarial; en realidad se considera un antipatrón ("modelo de dominio anémico") tener muy poca lógica de negocios allí.

  4. Si los controles GUI son parte de su modelo, absolutamente. Si no, es cuestionable.

  5. Definitivamente no es la vista. Se debe informar a la vista que los datos o el modelo han cambiado y reaccionar en consecuencia, pero el cambio real debe tener lugar en los datos o el modelo.

  6. En principio, probablemente sí. Poder deshacer comandos es una de las mejores cosas del patrón.

  7. La función execute () del comando definitivamente debe ser probada por la unidad. Nuevamente, si los comandos están vinculados a un modelo de algún tipo, se encuentran entre las partes más fáciles de probar de una aplicación.

  8. No estoy muy seguro de lo que quieres decir, pero:

    • Está perfectamente bien que una API externa use comandos como entrada o salida. Asumiendo que los valides, por supuesto.
    • "última capa" me suena como una vista. En MVC, los comandos probablemente deberían ser generados por el controlador y manejados por el modelo, por lo que sería sensato decir que la vista está construida "por encima" del comando, si eso es lo que estaba buscando.
    • En cuanto a las excepciones, probablemente no. En MVC, esperaría que el controlador sea el que capture las excepciones y las convierta en el tipo correcto de cuadro de diálogo. No el modelo / comandos.
  9. Absolutamente. La mayoría de los beneficios de los comandos son imposibles de implementar si el código nunca los ejecuta.

  10. Probablemente sea obvio por ahora, pero no los veo como mutuamente excluyentes en absoluto. En mi equipo de trabajo, el controlador traduce los eventos generados por el usuario en comandos, el modelo recibe y ejecuta los comandos (y guarda los comandos "deshacer" en algún lugar), y cuando se hace, el controlador le dice a la vista que se actualice a sí mismo en función del nuevo modelo . Los comandos también se envían a través de la red a cualquier copia del modelo que estén viendo otros usuarios, para que también lo vean. Funciona de manera brillante.

Y la pregunta del título: ¿Cuánta lógica poner en un comando? No le pondría ningún mínimo o máximo. Lo que diría es que es una muy buena idea implementar comandos más complejos utilizando una serie de comandos más simples y refactorizar los comandos de la misma manera que refactorizaría las funciones.

Ixrec
fuente