Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Command object pattern wannabe or the real thing?

The command object pattern is one that I still haven't been able to truly grasp and I found an implementation in the code I'm currently working on so I studied it long and hard to see if I could finally get it with a real world example. The problem is that I am sure this is not properly implemented and it is just an attempt by someone who just read about it and thought it made sense here.

Allow me to show it to you (for confidentiality reasons it will be greatly simplified but I'll do my best to show the main concepts):

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);
   }
}

The first thing that strikes me as odd is that these two classes are not inheriting from any abstract class nor implementing a common interface.

The code that uses these commands is as follows:

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

So the way I see it, this form is absolutely coupled to all the classes involved except the manager which is being injected to it through its constructors. So with this code I really don't see any benefit of creating the "command" classes which basically are just delegating the call to the manager's methods since the form is instantiating them when it needs them and calling the execute method right afterwards.

So could someone please explain what pieces, if any, is this implementation missing to truly be a command object pattern and, although it might be too subjective, what would be the benefit to implement it in this case?

Thank you.

like image 302
Sergio Romero Avatar asked Sep 08 '11 21:09

Sergio Romero


1 Answers

Based on what you're showing here it looks like the benefit of the command pattern is lost. There are a few reasons you might want to use the command pattern in the context of a WinForms app.

You want to execute a command later

public interface ICommand
{
    void Execute();
}

Keep a history of executed commands so they can be undone by the user

public interface ICommand
{
    void Execute();

    void Undo();
}

Check permissions to see if the current user has rights to execute the command. For example, maybe you have a RefundCustomerCommand and not all customer service agents have the right to issue a refund so you want to disable a button on the form.

public interface ICommand
{
    void Execute();

    bool CanExecute { get; }
}

You can also roll multiple commands together in a composite like this:

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

The command pattern also works nicely with the decorator. You can easily add additional cross-cutting behavior to your commands like retry logic:

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();
        }
    }
}
like image 95
Mike Valenty Avatar answered Nov 09 '22 11:11

Mike Valenty