66

我的问题与命令模式有关,我们有以下抽象(C# 代码):

public interface ICommand
{
    void Execute();
}

让我们使用一个简单的具体命令,该命令旨在从我们的应用程序中删除一个实体。以一个Person实例为例。

我会有一个DeletePersonCommand,它实现ICommand. 该命令需要Person删除作为参数,以便在Execute调用方法时将其删除。

管理参数化命令的最佳方法是什么?如何在执行命令之前将参数传递给命令?

4

13 回答 13

71

您需要通过构造函数或 setter 注入(或等效方法)将参数与命令对象相关联。也许是这样的:

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

     public void Execute()
     {
        doSomethingWith(personToDelete);
     }
}
于 2008-09-19T19:48:06.793 回答
23

通过构造函数或设置器传递数据是可行的,但需要命令的创建者知道命令需要的数据......

“上下文”的想法非常好,我正在研究(一个内部)框架,不久前就利用了它。

如果您设置控制器(与用户交互的 UI 组件、解释用户命令的 CLI、解释传入参数和会话数据的 servlet 等)以提供对可用数据的命名访问,命令可以直接请求它们想要的数据。

我真的很喜欢这样的设置允许的分离。考虑分层如下:

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

如果您“正确”地执行此操作,则可以将相同的命令和演示模型用于任何类型的用户界面。

更进一步,上面的“控制器”非常通用。UI 控件只需要知道它们将调用的命令的名称——它们(或控制器)不需要知道如何创建该命令或该命令需要什么数据。这才是这里真正的优势。

例如,您可以保存要在 Map 中执行的命令的名称。每当组件被“触发”(通常是 actionPerformed)时,控制器都会查找命令名称、实例化它、调用执行并将其推送到撤消堆栈(如果您使用的话)。

于 2008-09-25T22:21:27.497 回答
12

有一些选项:

您可以通过属性或构造函数传递参数。

其他选项可能是:

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

并将所有命令参数封装在一个值对象中。

于 2008-09-19T19:48:11.690 回答
8

我的实现是这样的(使用 Juanma 提出的 ICommand ):

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

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

IPersonService 可以是 IPersonRepository,这取决于您的命令是什么“层”。

于 2010-03-21T05:31:45.550 回答
6

创建命令对象时传递人员:

ICommand command = new DeletePersonCommand(person);

这样当你执行命令时,它就已经知道它需要知道的一切。

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

   public void Execute()
   {
      RealDelete(person);
   }
}
于 2008-09-19T19:53:23.817 回答
6

在这种情况下,我们对 Command 对象所做的是创建一个本质上是地图的 Context 对象。该映射包含名称值对,其中键是常量,值是命令实现使用的参数。如果您有一个命令链,其中后面的命令依赖于早期命令的上下文更改,则特别有用。

所以实际的方法变成了

void execute(Context ctx);
于 2008-09-19T19:55:00.520 回答
5

在构造函数中并存储为字段。

您还希望最终使您的 ICommands 可序列化以用于撤消堆栈或文件持久性。

于 2008-09-19T19:46:31.477 回答
3

基于 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();
            } 
于 2008-09-19T20:02:49.533 回答
3

已经提到的 Blair Conrad 的代码(不知道如何标记他)工作得很好,如果你知道在实例化类时要删除什么人并且他的方法就足够了。但是,如果你不知道你会删除,直到您按下按钮,您可以使用返回人员的方法引用来实例化命令。

   class DeletePersonCommand implements ICommand
{
     private Supplier<Person> personSupplier;

     public DeletePersonCommand(Supplier<Person> personSupplier)
     {
         this.personSupplier = personSupplier;
     }

     public void Execute()
     {
        personSupplier.get().delete();
     }
}

这样,在执行命令时,供应商会获取您要删除的人,并在执行时执行此操作。直到那时,该命令还没有关于删除谁的信息。

供应商上的有用链接

注意:用 java 编写的代码。有 c# 知识的人可以调整它。

于 2020-03-28T23:12:35.047 回答
2

您应该创建一个 CommandArgs 对象来包含您要使用的参数。使用 Command 对象的构造函数注入 CommandArgs 对象。

于 2008-09-21T15:43:14.507 回答
0

DeletePersonCommand 可以在其构造函数或方法中有参数。DeletePersonCommand 将具有 Execute() 并且在执行中可以检查将由 Getter/Setter 之前传递的 Execute() 调用的属性。

于 2008-09-19T19:47:04.470 回答
0

我会将任何必要的参数添加到DeletePersonCommand. 然后,当Execute()被调用时,使用在构造时传递给对象的那些参数。

于 2008-09-19T19:47:05.220 回答
-5

让“Person”实现某种 IDeletable 接口,然后使命令采用您的实体使用的任何基类或接口。这样,您可以创建一个 DeleteCommand,它会尝试将实体转换为 IDeletable,如果可行,请调用 .Delete

public class DeleteCommand : ICommand
{
   public void Execute(Entity entity)
   {
      IDeletable del = entity as IDeletable;
      if (del != null) del.Delete();
   }
}
于 2008-09-19T19:48:04.857 回答