Commands

Commands are used to separate the caller and the handler, i.e to create louse coupling.

The problem with the regular command pattern is that the command is coupled with the invoker, which makes it hard to let multiple handlers to take care of the command. It's also hard to reroute the command to somewhere else.

This implementation takes care of both problems.

Example

Let's say that you want to send a message, but you don't want to know HOW the message is sent. It can be sent as a SMS or what ever.

To begin with, we create a command class:
public SendMessageCmd : Command
{
  private User _to;
  private User _from;
  private string _subject;
  private string _body;

  public SendMessageCmd(User from, User to, string subject, string body)
  {
    Check.Require(from, "from");
    Check.Require(to, "to");
    Check.Require(subject, "subject");
    Check.Require(body, "body");
    // normally we use more validations, for instance subject length etc.
  
    _from = from; _to = to; _subject = subject; _body = body;
  }

  [....getters for the properties...]
}


To invoke a command, we only need to call Invoke in the dispatcher:
  SendMessageCmd cmd =new SendMessageCmd(me, bengt, "Hello", "Just testing the command implementation.");
  Program.Dispatcher.Invoke(cmd);
  if (!cmd.IsHandled)
    Console.WriteLine("No one handled our command :(");


Now we'll have to add some handlers too. We'll use a SMS gateway here...
public class SmsManager
{
  SmsGateway _gw;

  public SmsManager(CommandDispatcher dispatcher, SmsGateway gw)
  {
    _gw = gw;
    dispatcher.Add(typeof(SendMessageCmd), OnSendSms);
  }

  private bool OnSendSms(object source, CommandEventArgs args)
  {
    SendMessageCmd cmd = (SendMessageCmd)args.Command;
    _gw.Send(cmd.From.MobileNumber, cmd.To.MobileNumber, cmd.body);
  }
}


All this logic is on the server. But now we want to add messaging support to our GUI application too. The problem is that we do not want to handle the SendMessage command at client side, but in the server.
This is just as easy, we'll just have to use the CommandChannel.

Server side:
public static class Program
{
  static CommandServer _commandServer;
  [.. some code ..]

  private static void SetupCommandServer()
  {
      _commandServer = new CommandServer(_dispatcher);
      _commandServer.Start(IPAddress.Any, CommandChannel.DefaultPort);
  }
}


And client side:
public static class Program
{
    static CommandChannel _commandChannel;

  [..... some code. ...]

  
        private static void SetupCommandChannel()
        {
            _commandChannel = new CommandChannel();
            _commandChannel.Open(new IPEndPoint(IPAddress.Loopback, CommandChannel.DefaultPort));
            _commandChannel.AutoReconnect = true;
            _dispatcher.Unhandled += OnUnhandledCommand;
        }

        private static bool OnUnhandledCommand(object source, CommandEventArgs args)
        {
            _commandChannel.SendAndReply(args.Command);
            return args.Command.IsHandled;
        }
}


That's it. The client can now use all commands that you've defined. All commands that are not handled in the client are handled by the server. The command result is automatically transported to the client.

Last edited Aug 25, 2008 at 5:58 PM by jgauffin, version 2

Comments

No comments yet.