tags:

views:

309

answers:

4

Consider I'm interfacing with an external system that will send a message (DB table, message queue, web service) in some format. In the "message header" there is the "MessageType" that is a number from 1 to 20. The MessageType defines what to do with the rest of the message. There are things like new, modified, deleted, canceled...

My first inclination was to setup an enumeration and define all the types. Then parse the number into an enum type. With it as an enum I would setup the typical switch case system and call a particular method for each of the message types.

One big concern is maintenance.
A switch / case system is bulky and teadious but, it's really simple.
Various table / configuration systems can be difficult for someone else to grok and add new messages or tweak existing messages.

For 12 or so MessageTypes the switch/case system seems quite reasonable. What would be a reasonable cut-off point to switch to a table driven system?

What kinds of systems are considered best for handling these types of problems?

I'm setting a tag for both C# and Java here because it's definitly a common problem. There are many other languages with the same issue.

+4  A: 

In Java, you can make it an enum and give behaviour to the different values (although with 100 values, I'd hope that each type of behaviour is briefly, calling out to "proper" classes).

In C#, you can have a map from value to some appropriate delegate type - then when you statically construct the map, you can either use lambda expressions or method group conversions as appropriate.

Having said that, setting up the map is going to be just as ugly as a switch statement. If each switch statement is just a single method call, you might like to try this sort of format:

switch (messageType)
{
    case   0: HandleLogin(message);         break;
    case  50: SaveCurrentDocument(message); break;
    case 100: HandleLogout(message);        break;
}

(etc). I know it's against normal conventions, but it can be quite neat for the odd exceptional situation like this. If you only need the numbers in one place, then there's little point in introducing constants - basically the line containing the number effectively is the constant definition!

Jon Skeet
+1  A: 

How about having a Dictionary<MessageType, ProcessMessageDelegate> to store those methods by their message types? During initialization of the class, register all the methods in this dictionary. Then call the appropriate method. Following is the pseudo code:

  delegate void ProcessMessageDelegate(Message message)

  public class MyMessageProcessor
  {
    Dictionary<int, ProcessMessageDelegate> methods;

    public void Register( int messageType, 
                          ProcessMessageDelegate processMessage)
    {
      methods[messageType] = processMessage;
    }

    public void ProcessMessage(int messageType, Message message)
    {
      if(methods.ContainsKey(messageType))
      {
        methods[messageType](message);
      }
    }
  }

To register methods:

   myProcessor.Register(0, ProcessMessageOfType0);
   myProcessor.Register(1, ProcessMessageOfType1);
   myProcessor.Register(2, ProcessMessageOfType2);
   ...

Edit: I realized Jon already suggests having a map which now makes my answer redundant. But I don't understand why a statically constructed map is uglier than switch case?

Vivek
A: 

This is how I've done this in C#.

I think this approach is actually not so ugly as all that, It gets less ugly as the number of message types increases: to implement a new message type, you just have to add a value to your Enum and mark the new message handler class with an attribute.

And there are circumstances where being able to load your message handlers from an assembly at runtime is a very powerful feature; you can have a single executable that behaves differently based on which message-handler assembly is installed.

Start by creating an interface for the message handler (we'll call it IMessageHandler) and an Enum for the message type (we'll call it MessageType).

Next, create a class called MessageHandlerAttribute:

public class MessageHandlerAttribute : System.Attribute
{
    public MessageType MessageType { get; set; }
}

Now implement each message handler as a separate class, and mark each class with its message type attribute. If a message handler can handle multiple types of message, you can put multiple attributes on it:

[MessageHandler(MessageType=MessageType.Login)]
public class LoginMessageHandler : IMessageHandler
{
    ...
}

It's important that these message handlers all have parameterless constructors. I can't think of a good reason that you'd want a message handler's constructor to take parameters, but if any does, the code below can't handle it.

Build all the message handlers into the same assembly, and be sure you have a way to know its path at runtime. (That's the big point of failure for this approach.)

Now we can use Reflection to build a map of message handlers at runtime:

using System.Reflection;
...
Assembly mhAssembly = Assembly.LoadFrom(mhAssemblyPath);
Dictionary<MessageType, IMessageHandler> mhMap = new Dictionary<MessageType, IMessageHandler>();
foreach (Type t in mhAssembly.GetExportedTypes())
{
   if (t.GetInterface("IMessageHandler") != null)
   {
      MessageHandlerAttribute list = (MessageHandlerAttribute[])t.GetCustomAttributes(
         typeof(MessageHandlerAttribute), false);
      foreach (MessageHandlerAttribute att in list)
      {
         MessageType mt = att.MessageType;
         Debug.Assert(!mhMap.ContainsKey(mt));
         IMessageHandler mh = mhAssembly.CreateInstance(
            t.FullName,
            true,
            BindingFlags.CreateInstance,
            null,
            new object[] { },
            null,
            null);
         mhMap.Add(mt, mh);
      }
   }
   // depending on your application, you might want to check mhMap now to make
   // sure that every MessageType value is in it.
}
return mhMap;

Now when you get a message, you can handle it like this:

Debug.Assert(MhMap.ContainsKey(Message.MessageType));
IMessageHandler mh = MhMap[Message.MessageType];
mh.HandleMessage(Message);

This code's all based on code I have in a production system right now; I've changed it slightly (so that the message handlers implement an interface instead of deriving from an abstract class, and that it handles multiple message handler attributes), which has probably introduced bugs into it.

Robert Rossney
+1  A: 

Have a handler interface like this:

interface MessageHandler {
   void processMessage(Message msg) throws Exception;
   int[] queryInterestingMessageIds();
   int queryPriority(int messageId); // this one is optional
}

Locate, instantiate and register your handlers. You might want to use some reflection-based mechanism like ServiceLoader, Spring (explicit config or classpath scanning amd possibly autowiring) or plain properties file

The registration should pass each handler to a WhateverManager class which would hold internally a map (or arry indexed by message ID) of collections of handlers. If you expect to have multiple handlers, you can use the queryPriority(int) method to resolve the order of handling (otherwise you can treat it as an error and throw an exception at config time). It's a good practice to NOT use static map for registration.

If you decide to support multiple handlers for a message, you might want to daisychain them. In such case one way is to change the signature as follows:

 Message processMessage(Message msg, Message original) throws Exception;
ddimitrov