命令模式:如何将参数传递给命令?

时间:2008-09-19 19:44:20

标签: c# design-patterns

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

public interface ICommand
{
    void Execute();
}

让我们来看一个简单的具体命令,它旨在从我们的应用程序中删除一个实体。例如,Person个实例。

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

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

13 个答案:

答案 0 :(得分:60)

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

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

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

答案 1 :(得分:20)

通过构造函数或setter传递数据,但需要命令的创建者知道命令所需的数据...

“上下文”的想法非常好,而且我正在研究(一个内部的)框架,并在一段时间内利用它。

如果设置控制器(与用户交互的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)时,控制器会查找命令名称,实例化它,调用execute,并将其推送到撤消堆栈(如果使用的话)。

答案 2 :(得分:10)

有一些选择:

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

其他选项可能是:

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

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

答案 3 :(得分:6)

在创建命令对象时传递此人:

ICommand command = new DeletePersonCommand(person);

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

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

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

答案 4 :(得分:6)

我的实现是这样的(使用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,它取决于你的命令是什么“层”。

答案 5 :(得分:5)

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

因此实际方法变为

void execute(Context ctx);

答案 6 :(得分:4)

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

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

答案 7 :(得分: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();
            } 

答案 8 :(得分:2)

已经提到过来自布莱尔·康拉德(Blair Conrad)的代码(不知道如何标记他)可以很好地工作如果您知道在实例化该类时要删除的人,并且他的方法就足够了。 ,如果您不知道要删除谁,直到您按下按钮,就可以使用返回人员的方法引用实例化该命令。

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

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

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

以这种方式执行命令时,供应商会在执行时获取您要删除的人。直到那个时候,该命令还没有删除谁的信息。

在供应商上使用完整link

注意:用Java语言编写的代码。有c#知识的人可以对其进行调整。

答案 9 :(得分:1)

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

答案 10 :(得分:0)

DeletePersonCommand可以在其构造函数或方法中包含参数。 DeletePersonCommand将具有Execute()和执行可以检查属性,该属性将由Getter / Setter先前调用Execute()。

答案 11 :(得分:0)

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

答案 12 :(得分:-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();
   }
}