Command Pattern : How to pass parameters to a command?

空扰寡人 提交于 2019-12-17 17:33:25

问题


My question is related to the command pattern, where we have the following abstraction (C# code) :

public interface ICommand
{
    void Execute();
}

Let's take a simple concrete command, which aims to delete an entity from our application. A Person instance, for example.

I'll have a DeletePersonCommand, which implements ICommand. This command needs the Person to delete as a parameter, in order to delete it when Execute method is called.

What is the best way to manage parametrized commands ? How to pass parameters to commands, before executing them ?


回答1:


You'll need to associate the parameters with the command object, either by constructor or setter injection (or equivalent). Perhaps something like this:

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

     public void Execute()
     {
        doSomethingWith(personToDelete);
     }
}



回答2:


Passing the data in via a constructor or setter works, but requires the creator of the command to know the data the command needs...

The "context" idea is really good, and I was working on (an internal) framework that leveraged it a while back.

If you set up your controller (UI components that interact with the user, CLI interpreting user commands, servlet interpreting incoming parameters and session data, etc) to provide named access to the available data, commands can directly ask for the data they want.

I really like the separation a setup like this allows. Think about layering as follows:

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

If you do this "right", the same commands and presentation model can be used with any type of user interface.

Taking this a step further, the "controller" in the above is pretty generic. The UI controls only need to know the name of the command they'll invoke -- they (or the controller) don't need to have any knowledge of how to create that command or what data that command needs. That's the real advantage here.

For example, you could hold the name of the command to execute in a Map. Whenever the component is "triggered" (usually an actionPerformed), the controller looks up the command name, instantiates it, calls execute, and pushes it on the undo stack (if you use one).




回答3:


There are some options:

You could pass parameters by properties or constructor.

Other option could be:

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

And encapsulate all command parameters in a value object.




回答4:


My implementation would be this (using the ICommand proposed by Juanma):

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

    public void Execute(Person person)
    {
        this.personService.DeletePerson(person);
    }
}

IPersonService could be an IPersonRepository, it depends in what "layer" your command is.




回答5:


Pass the person when you create the command object:

ICommand command = new DeletePersonCommand(person);

so that when you execute the command, it already knows everything that it needs to know.

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

   public void Execute()
   {
      RealDelete(person);
   }
}



回答6:


In the constructor and stored as fields.

You will also want to eventually make your ICommands serializable for the undo stack or file persistence.




回答7:


In this case, what we've done with our Command objects is to create a Context object which is essentially a map. The map contains name value pairs where the keys are constants and the values are parameters that are used by the Command implementations. Especially useful if you have a Chain of Commands where later commands depend on context changes from earlier commands.

So the actual method becomes

void execute(Context ctx);



回答8:


Based on the pattern in C#/WPF the ICommand Interface (System.Windows.Input.ICommand) is defined to take an object as a parameter on the Execute, as well as the CanExecute method.

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

This allows you to define your command as a static public field which is an instance of your custom command object that implements ICommand.

public static ICommand DeleteCommand = new DeleteCommandInstance();

In this way the relevant object, in your case a person, is passed in when execute is called. The Execute method can then cast the object and call the Delete() method.

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



回答9:


You should create a CommandArgs object to contain the parameters you want to use. Inject the CommandArgs object using the constructor of the Command object.




回答10:


DeletePersonCommand can have parameter in its constructor or methods . DeletePersonCommand will have the Execute() and in the execute can check attribute that will be passed by Getter/Setter previously the call of the Execute().




回答11:


I would add any necessary arguments to the constructor of DeletePersonCommand. Then, when Execute() is called, those parameters passed into the object at construction time are used.




回答12:


Have "Person" implement some sort of IDeletable interface, then make the command take whatever base class or interface your entities use. That way, you can make a DeleteCommand, which tries to cast the entity to an IDeletable, and if that works, call .Delete

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


来源:https://stackoverflow.com/questions/104918/command-pattern-how-to-pass-parameters-to-a-command

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!