Шаблон команды: Как передать параметры команде? - PullRequest
57 голосов
/ 19 сентября 2008

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

public interface ICommand
{
    void Execute();
}

Давайте возьмем простую конкретную команду, которая направлена ​​на удаление объекта из нашего приложения. Например, Person.

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

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

Ответы [ 12 ]

60 голосов
/ 19 сентября 2008

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

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

     public void Execute()
     {
        doSomethingWith(personToDelete);
     }
}
20 голосов
/ 26 сентября 2008

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

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

Если вы настроили свой контроллер (компоненты пользовательского интерфейса, которые взаимодействуют с пользователем, 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), контроллер ищет имя команды, создает его экземпляр, вызывает execute и помещает его в стек отмены (если вы его используете).

10 голосов
/ 19 сентября 2008

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

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

Другой вариант может быть:

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

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

6 голосов
/ 21 марта 2010

Моя реализация будет такой (с использованием 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 голосов
/ 19 сентября 2008

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

ICommand command = new DeletePersonCommand(person);

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

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

   public void Execute()
   {
      RealDelete(person);
   }
}
5 голосов
/ 19 сентября 2008

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

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

void execute(Context ctx);
4 голосов
/ 19 сентября 2008

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

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

3 голосов
/ 20 сентября 2008

На основе шаблона в C # / 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();
            } 
1 голос
/ 21 сентября 2008

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

0 голосов
/ 19 сентября 2008

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

public class DeleteCommand : ICommand
{
   public void Execute(Entity entity)
   {
      IDeletable del = entity as IDeletable;
      if (del != null) del.Delete();
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...