strategy patterns pattern gof dofactory book c# design-patterns

patterns - state pattern c#



Patrón de comando: cómo pasar parámetros a un comando? (12)

Mi pregunta está relacionada con el patrón de comando, donde tenemos la siguiente abstracción (código C #):

public interface ICommand { void Execute(); }

Tomemos un comando concreto simple, que tiene como objetivo eliminar una entidad de nuestra aplicación. Una instancia de Person , por ejemplo.

Tendré un DeletePersonCommand , que implementa ICommand . Este comando necesita que la Person elimine como parámetro, para eliminarlo cuando se Execute método Execute .

¿Cuál es la mejor manera de administrar comandos parametrizados? ¿Cómo pasar parámetros a los comandos antes de ejecutarlos?


Debe crear un objeto CommandArgs para contener los parámetros que desea usar. Inyecte el objeto CommandArgs utilizando el constructor del objeto Command.


DeletePersonCommand puede tener un parámetro en su constructor o métodos. DeletePersonCommand tendrá el atributo Execute () y en el ejecutar puede verificar que Getter / Setter pasará previamente a la llamada de Execute ().


En el constructor y almacenados como campos.

También querrás eventualmente hacer tus ICommands serializables para la pila de deshacer o la persistencia del archivo.


En este caso, lo que hemos hecho con nuestros objetos Command es crear un objeto Context que es esencialmente un mapa. El mapa contiene pares de valores de nombre donde las claves son constantes y los valores son parámetros que utilizan las implementaciones de Comando. Especialmente útil si tiene una Cadena de comandos donde los comandos posteriores dependen de los cambios de contexto de los comandos anteriores.

Entonces el método actual se convierte

void execute(Context ctx);


Haga que "Persona" implemente algún tipo de interfaz IDeletable, luego haga que el comando tome la clase base o interfaz que usen sus entidades. De esta forma, puede crear un DeleteCommand, que intenta convertir la entidad en un IDeletable, y si eso funciona, llame a .Delete

public class DeleteCommand : ICommand { public void Execute(Entity entity) { IDeletable del = entity as IDeletable; if (del != null) del.Delete(); } }


Hay algunas opciones:

Puede pasar parámetros por propiedades o constructor.

Otra opción podría ser:

interface ICommand<T> { void Execute(T args); }

Y encapsula todos los parámetros de comando en un objeto de valor.


Mi implementación sería esta (usando el ICommand propuesto por Juanma):

public class DeletePersonCommand: ICommand<Person> { public DeletePersonCommand(IPersonService personService) { this.personService = personService; } public void Execute(Person person) { this.personService.DeletePerson(person); } }

IPersonService podría ser un IPersonRepository, depende de qué "capa" sea su comando.


Pasar los datos a través de un constructor o setter funciona, pero requiere que el creador del comando conozca los datos que el comando necesita ...

La idea del "contexto" es realmente buena, y estuve trabajando en un marco (interno) que lo aprovechó hace un tiempo.

Si configura su controlador (componentes de UI que interactúan con el usuario, CLI interpretando comandos de usuario, servlet interpretando parámetros entrantes y datos de sesión, etc.) para proporcionar acceso con nombre a los datos disponibles, los comandos pueden solicitar directamente los datos que desean.

Realmente me gusta la separación que permite una configuración como esta. Piense en capas de la siguiente manera:

User Interface (GUI controls, CLI, etc) | [syncs with/gets data] V Controller / Presentation Model | ^ [executes] | V | Commands --------> [gets data by name] | [updates] V Domain Model

Si haces esto "a la derecha", los mismos comandos y el mismo modelo de presentación se pueden usar con cualquier tipo de interfaz de usuario.

Llevando esto un paso más allá, el "controlador" de arriba es bastante genérico. Los controles de la interfaz de usuario solo necesitan saber el nombre del comando que invocarán; ellos (o el controlador) no necesitan tener ningún conocimiento de cómo crear ese comando o qué datos necesita ese comando. Esa es la verdadera ventaja aquí.

Por ejemplo, puede mantener el nombre del comando para ejecutar en un Mapa. Cada vez que el componente se "activa" (generalmente una acción realizada), el controlador busca el nombre del comando, lo instancia, llama a ejecutar y lo empuja a la pila de deshacer (si usa uno).


Pase a la persona cuando crea el objeto de comando:

ICommand command = new DeletePersonCommand(person);

de modo que cuando ejecutas el comando, ya sabe todo lo que necesita saber.

class DeletePersonCommand : ICommand { private Person person; public DeletePersonCommand(Person person) { this.person = person; } public void Execute() { RealDelete(person); } }


Según el patrón en C # / WPF, la interfaz de ICommand (System.Windows.Input.ICommand) se define para tomar un objeto como parámetro en Execute, así como en el método CanExecute.

interface ICommand { bool CanExecute(object parameter); void Execute(object parameter); }

Esto le permite definir su comando como un campo público estático que es una instancia de su objeto de comando personalizado que implementa ICommand.

public static ICommand DeleteCommand = new DeleteCommandInstance();

De esta forma, el objeto relevante, en su caso una persona, se transfiere cuando se ejecuta execute. El método Execute puede lanzar el objeto y llamar al método Delete ().

public void Execute(object parameter) { person target = (person)parameter; target.Delete(); }


Tendrá que asociar los parámetros con el objeto de comando, ya sea mediante el constructor o la inyección del colocador (o equivalente). Quizás algo como esto:

public class DeletePersonCommand: ICommand { private Person personToDelete; public DeletePersonCommand(Person personToDelete) { this.personToDelete = personToDelete; } public void Execute() { doSomethingWith(personToDelete); } }


DeletePersonCommand todos los argumentos necesarios al constructor de DeletePersonCommand . Luego, cuando se Execute() , esos parámetros pasaron al objeto en el momento de la construcción.