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

发布于 2024-07-05 21:51:23 字数 383 浏览 11 评论 0原文

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

public interface ICommand
{
    void Execute();
}

让我们采用一个简单的具体命令,其目的是从我们的应用程序中删除一个实体。 例如,一个 Person 实例。

我将拥有一个实现 ICommandDeletePersonCommand。 该命令需要将要删除的Person作为参数,以便在调用Execute方法时将其删除。

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

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 ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

半世蒼涼 2024-07-12 21:51:24

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

所以实际的方法就变成了

void execute(Context ctx);

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);
长发绾君心 2024-07-12 21:51:24

创建命令对象时传递 person:

ICommand command = new DeletePersonCommand(person);

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

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

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

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);
   }
}
大姐,你呐 2024-07-12 21:51:24

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

您还需要最终使 ICommand 可序列化,以实现撤消堆栈或文件持久性。

In the constructor and stored as fields.

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

对岸观火 2024-07-12 21:51:24

已经提到的 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# 知识的人可以对其进行调整。

Already mentioned code from Blair Conrad(don't know how to tag him) works perfectly fine if you know what person you want to delete when you instantiate the class and his method would suffice.But,if you don't know who you gonna delete until you press the button you can instantiate the command using a method reference that returns the person.

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

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

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

That way when the command is executed the supplier fetches the person you want to delete,doing so at the point of execution. Up until that time the command had no information of who to delete.

Usefull link on the supplier.

NOTE:code writen in java . Someone with c# knowledge can tune that.

游魂 2024-07-12 21:51:24

基于 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();
            } 

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();
            } 
一梦浮鱼 2024-07-12 21:51:24

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

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.

一紙繁鸢 2024-07-12 21:51:24

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

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().

愛上了 2024-07-12 21:51:24

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

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.

始终不够爱げ你 2024-07-12 21:51:24

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

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

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();
   }
}
幻想少年梦 2024-07-12 21:51:23

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

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

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

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);
     }
}
谜兔 2024-07-12 21:51: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 控件只需要知道它们将调用的命令的名称 - 它们(或控制器)不需要了解如何创建该命令或该命令包含哪些数据需要。 这才是真正的优势。

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

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).

爱本泡沫多脆弱 2024-07-12 21:51:23

有一些选项:

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

其他选项可以是:

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

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

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.

长伴 2024-07-12 21:51:23

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文