命令对象模式想要还是真实的东西?

时间:2011-09-08 21:30:29

标签: c# design-patterns

命令对象模式是我仍然无法真正掌握的模式,我在我正在编写的代码中找到了一个实现,所以我研究了很长时间,很难看出我是否能够最终得到它一个现实世界的例子。问题是我确信这没有得到适当的实施,只是一个刚刚阅读过它的人尝试并认为它在这里有意义。

请允许我向您展示(出于保密原因,它会大大简化,但我会尽力展示主要概念):

public class CommandOne
{
   public CommandOne(Manager manager, MyForm form)
   {
      m_manager = manager;
      m_form = form;
   }

   public void Execute()
   {
       m_manager.CommandOne(m_form);
   }
}

public class CommandTwo
{
   public CommandTwo(Manager manager, MyForm form)
   {
      m_manager = manager;
      m_form = form;
   }

   public void Execute()
   {
       m_manager.CommandTwo(m_form);
   }
}

令我感到奇怪的第一件事是,这两个类不是从任何抽象类继承,也不是实现公共接口。

使用这些命令的代码如下:

public class MyForm : System.Windows.Forms.Form
{
   public MyForm(Manager manager)
   {
      m_manager = manager;
   }

   private void SomeMethod()
   {
      ....
      var cmd = new CommandOne(manager, this);
      cmd.Execute();
      ...
   }

   private void OtherMethod()
   {
      ....
      var cmd = new CommandTwo(manager, this);
      cmd.Execute();
      ...
   }
}

所以我看到它的方式,这个表单绝对耦合到所有涉及的类,除了通过其构造函数注入它的管理器。所以使用这段代码我真的没有看到创建“命令”类的任何好处,这些类基本上只是委托调用管理器的方法,因为表单在需要时实例化它们并且之后调用execute方法。

那么有人可以解释一下,如果有的话,这个实现缺少真正的命令对象模式,尽管它可能过于主观,在这种情况下实现它会有什么好处?

谢谢。

1 个答案:

答案 0 :(得分:11)

根据您在此处显示的内容,看起来命令模式的好处已丢失。您可能希望在WinForms应用程序的上下文中使用命令模式有几个原因。

您想稍后执行命令

public interface ICommand
{
    void Execute();
}

保留已执行命令的历史记录,以便用户可以撤消

public interface ICommand
{
    void Execute();

    void Undo();
}

检查权限以查看当前用户是否有权执行该命令。例如,您可能拥有RefundCustomerCommand并且并非所有客户服务代理都有权发出退款,因此您要禁用表单上的按钮。

public interface ICommand
{
    void Execute();

    bool CanExecute { get; }
}

您还可以在这样的组合中滚动多个命令:

public class CompositeCommand : ICommand
{
    private readonly List<ICommand> commands;

    public CompositeCommand()
    {
        commands = new List<ICommand>();
    }

    public void Add(ICommand command)
    {
        commands.Add(command);
    }

    public void Execute()
    {
        foreach (var command in commands) command.Execute();
    }
}

命令模式也可以很好地与装饰器配合使用。您可以轻松地为命令添加其他横切行为,例如重试逻辑:

public class RetryOnTimeout : ICommand
{
    private readonly ICommand command;
    private int numberOfRetries;

    public RetryOnTimeout(ICommand command, int numberOfRetries)
    {
        this.command = command;
        this.numberOfRetries = numberOfRetries;
    }

    public void Execute()
    {
        try
        {
            command.Execute();
        }
        catch (TimeoutException)
        {
            if (++numberOfRetries > 3)
                throw;

            Execute();
        }
    }
}