Подтвердить что ты не робот

Command Pattern: Как передать параметры команде?

Мой вопрос связан с шаблоном команды, где у нас есть следующая абстракция (код С#):

public interface ICommand
{
    void Execute();
}

Возьмем простую конкретную команду, цель которой - удалить объект из нашего приложения. Пример Person, например.

У меня будет DeletePersonCommand, который реализует ICommand. Эта команда нуждается в Person для удаления в качестве параметра, чтобы удалить ее, когда вызывается метод Execute.

Каков наилучший способ управления параметризованными командами? Как передать параметры командам перед их выполнением?

4b9b3361

Ответ 1

Вам нужно будет связать параметры с объектом команды либо путем инсталляции конструктора, либо с установкой (или эквивалентом). Возможно, что-то вроде этого:

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

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

Ответ 2

Передача данных через конструктор или сеттер работает, но требует, чтобы создатель команды знал данные, необходимые команде...

Идея "контекста" действительно хороша, и я работал над (внутренней) структурой, которая помогала ей некоторое время назад.

Если вы настроили свой контроллер (компоненты пользовательского интерфейса, которые взаимодействуют с пользователем, интерпретируют пользовательские команды CLI, сервлет интерпретируют входящие параметры и данные сеанса и т.д.), чтобы предоставить именованный доступ к доступным данным, команды могут напрямую запрашивать данные, которые они хотите.

Мне очень нравится разделение, подобное этому позволяет установка. Подумайте о расслоении следующим образом:

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

Если вы сделаете это "правильно", те же команды и модель представления могут использоваться с любым типом пользовательского интерфейса.

Принимая этот шаг дальше, "контроллер" в приведенном выше примере довольно общий. Элементы управления пользовательским интерфейсом должны знать только имя команды, которую они вызывают - им (или контроллеру) не нужно знать, как создать эту команду или какие данные требуется этой команде. Это реальное преимущество здесь.

Например, вы можете сохранить имя команды для выполнения на карте. Всякий раз, когда компонент "запускается" (как правило, actionPerformed), контроллер просматривает имя команды, создает экземпляр, вызывает выполнение и толкает его в стек отмены (если вы его используете).

Ответ 3

Есть несколько вариантов:

Вы можете передавать параметры по свойствам или конструктору.

Другой вариант:

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

И инкапсулируйте все параметры команды в объект значения.

Ответ 4

Передайте человеку при создании объекта команды:

ICommand command = new DeletePersonCommand(person);

чтобы при выполнении команды он уже знал все, что ему нужно знать.

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

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

Ответ 5

Моя реализация была бы такой (используя ICommand, предложенную Хуанмой):

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

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

IPersonService может быть IPersonRepository, это зависит от того, в каком слое ваша команда.

Ответ 6

В этом случае то, что мы сделали с нашими объектами Command, - это создать объект Context, который по существу является картой. Карта содержит пары значений имени, где ключи являются константами, а значения - это параметры, которые используются реализациями Command. Особенно полезно, если у вас есть цепочка команд, где более поздние команды зависят от изменений контекста от более ранних команд.

Таким образом, фактический метод становится

void execute(Context ctx);

Ответ 7

В конструкторе и сохраняется как поля.

Вы также захотите в конечном итоге сделать ваши ICommands сериализуемыми для стека отмены или сохранения файлов.

Ответ 8

На основе шаблона в С#/WPF интерфейс ICommand (System.Windows.Input.ICommand) определен для принятия объекта как параметра в Execute, а также метода CanExecute.

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

Это позволяет вам определить вашу команду как статическое общедоступное поле, которое является экземпляром вашего пользовательского объекта команды, который реализует ICommand.

public static ICommand DeleteCommand = new DeleteCommandInstance();

Таким образом, соответствующий объект, в вашем случае человек, передается при вызове execute. Затем метод Execute может передать объект и вызвать метод Delete().

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

Ответ 9

Вы должны создать объект CommandArgs, чтобы содержать параметры, которые вы хотите использовать. Внесите объект CommandArgs с помощью конструктора объекта Command.

Ответ 10

DeletePersonCommand может иметь параметр в своем конструкторе или методах. У DeletePersonCommand будет Execute(), и в execute может проверить атрибут, который будет передан Getter/Setter ранее вызовом функции Execute().

Ответ 11

Я бы добавил любые необходимые аргументы в конструктор DeletePersonCommand. Затем, когда вызывается Execute(), используются те параметры, которые передаются в объект во время построения.

Ответ 12

Попросите "Личность" реализовать какой-то интерфейс IDeletable, а затем заставьте команду взять любой базовый класс или интерфейс, который использует ваши сущности. Таким образом, вы можете сделать DeleteCommand, который пытается передать объект в IDeletable, и если это работает, вызовите .Delete

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