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 Execute
mé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):
- ¿Está bien mostrar cuadros de mensajes o cuadros de diálogo para abrir archivos, etc.?
- ¿Está bien establecer propiedades de cualquier objeto?
- ¿Puede un comando contener lógica empresarial?
- ¿Puede un comando modificar los controles de la GUI de todos modos?
- ¿A qué comandos de capa pertenecen? Vista o capa de datos? ¿Puedo tener comandos en ambas capas?
- ¿Puede un comando hacer todo lo que anteriormente estaba en el
button1_Click
? - ¿Debería un comando por unidad comprobable?
- ¿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?
- ¿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?
- ¿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.
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:
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.
Mientras ese objeto sea accesible desde cualquier lugar que desee ejecutar el comando, entonces, por supuesto, está bien cambiar sus propiedades.
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í.
Si los controles GUI son parte de su modelo, absolutamente. Si no, es cuestionable.
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.
En principio, probablemente sí. Poder deshacer comandos es una de las mejores cosas del patrón.
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.
No estoy muy seguro de lo que quieres decir, pero:
Absolutamente. La mayoría de los beneficios de los comandos son imposibles de implementar si el código nunca los ejecuta.
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.
fuente