2

Following is the state I want to achieve in a clean way: The wish state

As you can see, I have the problem that the Invoker only knows the Command interface, but want to invoke the ConcreteCommand. This implementation again needs some arguments from the Invoker.

Here is now the dirty solution I have used so far: The current state

There are several problems I have with this solution:

  • The Invoker has meta knowledge about the count and the types of the arguments of the commands.
  • The Invoker uses magic strings to get the desired command.
  • When the Invoker do not pass the correct arguments I get a runtime error. I would prefer compile errors.

What is the common approach to pass arguments to concrete commands, without breaking the loose coupling (e.g. the Invoker needs to know the ConcreteCommand)?

Regards, Yggdrasil

Yggdrasil
  • 1,377
  • 2
  • 13
  • 27

4 Answers4

1

Use some kind of Factory to create/access the ConcreteCommands?

This way the Invoker only knows the Command interface, and the Factory knows how to pair it with a concrete implementation.

public class Factory {
  // Find by key, can vary implementation at runtime
  Command makeCommand(final String key);

  // Compile-safe invocation, but you can still vary implementation at runtime
  Command makeMySpecificCommand(); 
}
Anders Johansen
  • 10,165
  • 7
  • 35
  • 52
  • This is similar to my implementation. The only problem that I could solve with this are the magic strings. The problem with the arguments are not solved. – Yggdrasil Nov 18 '13 at 12:56
  • 1
    If the arguments change from Command to Command, then the answer is perhaps to use Double Dispatch aka Visitor pattern. You have to provide SOME mapping of your intention at the Invoker point, so some translation will be needed. Visitor takes care of different parameter signatures... – Anders Johansen Nov 18 '13 at 12:59
  • I don't know if I understand you correctly. Could you please provide a simple example how to use the visitor pattern here? – Yggdrasil Nov 18 '13 at 13:30
  • http://stackoverflow.com/questions/255214/when-should-i-use-the-visitor-design-pattern is a good overview of the uses of Visitor pattern. – Anders Sewerin Johansen Nov 18 '13 at 14:47
  • Here's the thing: Your Invoker will need to know what it wants the command to do, so the /intention/ is determined there. You want to separate the intention from the actual details of performing it by not exposing all the concrete commands to the invoker. That means that you need some other mechanism for translating intent to concrete command. Magic strings as light-weight, but error prone (typos cause run time failures). The second option is a factory or facade. The third option is Visitor. Second option is probably what you want. – Anders Johansen Nov 19 '13 at 06:56
  • ...because it allows you to have the compiler find typos for you, and to hide implementation from the Invoker, and it lets the factory do dependency injection, hot plugging or just creating complex commands from achain of simpler ones using Composite or Chain of Responsibility. – Anders Johansen Nov 19 '13 at 06:58
1

If your Invoker needs to send arguments to command and you really want to do it in type-safe way(which is not usually so easy if we are dealing with composition if objects rather than inheritance), then I would parameterize Invoker with type of arguments-parameters that it can call, so that now Invoker may call commands that support such type of parameters. Sthm like that.

public abstract class Command<T> where T:class
{
    public abstract void Execute(T par);
}

public class ConcreteCommand<T> : Command<T> where T : class
{
    private readonly Receiver<T> _receiver;

    public ConcreteCommand(Receiver<T> receiver)
    {
        _receiver = receiver;
    }

    #region Overrides of Command<T>
    public override void Execute(T par)
    {
        _receiver.MenuItemClick(par);
    }
    #endregion
}

public class Invoker<T> where T : class
{
    private readonly T par;
    private readonly Command<T> cmd;

    public Invoker(T par, Command<T> cmd)
    {
        this.par = par;
        this.cmd = cmd;
    }

    public void Invoke()
    {
        cmd.Execute(par);
    }
}

public class Receiver<T> where T : class 
{
    public void MenuItemClick(T e)
    {
        Console.WriteLine("Parameter types {0}", e.GetType().FullName);
    }
}

static internal class CmdBuilder
{
    public static Command<T> PrepareCommand<T>() where T : class
    {
        Receiver<T> rcv = new Receiver<T>();
        Command<T> cmd = new ConcreteCommand<T>(rcv);
        return cmd;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var cmd = CmdBuilder.PrepareCommand<EventArgs>();
        cmd.Execute(new EventArgs());

        Console.ReadKey(true);
    }
}

Other option is to use parameter-object. Since all commands "know" what type of parameters they are expecting they can cast it to specific realization of parameter-object. But that wouldn't give you compile errors

nikita
  • 2,737
  • 2
  • 20
  • 26
  • This is interesting. If I understand it correctly every command would have its own public `Argument` class. I could than even ask for the Command with the specific `Argument` type rather than with magic strings. This might be a solution. – Yggdrasil Nov 18 '13 at 13:37
  • The question is, if it is a good approach to pass the arguments to the execute method. The pure command pattern says, that the execute method has no arguments, they are passed in the constructor. This again could be problematic if I need a fast and often called `canExecute`. Than it would be disadvantageous to always create a new object. – Yggdrasil Nov 18 '13 at 13:45
  • 1
    @Yggdrasil updated answer showing how command builder can be made. GoF also have many examples where according to concrete realization and goals patterns are changed, they are no dogma. As for speed - patterns don't usually give extra speed. Flexibility yes, but no speed. – nikita Nov 18 '13 at 13:54
0

I can see 2 option here. First you may pass an argument in ConcreteCommand constructor and keep it as a property. Later Execute() method will use that property. the other option if you want to pass an argument from Invoker directly. You may change the ICommand interface to this Execute(IArgument arg) Each argument will be inherited from IArgument and lately will be converted to original type in ConcreteCommand implementation. if the current ConcreteCommand implementation cannot convert the argument to original type, say, ArgumentImpl it will skip Execute and yield to other ConcreteCommand implementation

Arseny
  • 7,251
  • 4
  • 37
  • 52
0

Instead of sending arguments to the 'execute' method, you can pass them while creating the command instance.

public class Factory {

  Command createCommand(Object[] objs,String key){
     return new ConcreteCommand(objs,key);
  }

}