Design pattern for handling multiple message types

前端 未结 10 1043
甜味超标
甜味超标 2020-11-28 03:13

I\'ve got the GOF sitting on my desk here and I know there must be some kind of design pattern that solves the problem I\'m having, but man I can\'t figure it out.

F

相关标签:
10条回答
  • 2020-11-28 03:51

    In a similar scenario I have a server which receives lots of different messages from multiple clients.

    All messages are sent serialized and start with an identifier of message type. I then have a switch statement looking at the identifier. The messages are then deserialized (to very differing objects) and processed as appropriate.

    A similar thing could be done by passing objects which implement an interface which includes a way of indicating message type.

    public void ProcessMessage(IMessage msg)
    {
        switch(msg.GetMsgType())  // GetMsgType() is defined in IMessage
        {
            case MessageTypes.Order:
                ProcessOrder(msg as OrderMessage);  // Or some other processing of order message
                break;
            case MessageTypes.Trade:
                ProcessTrade(msg as TradeMessage); //  Or some other processing of trade message
            break;
            ...
        }
    }
    
    0 讨论(0)
  • 2020-11-28 03:52

    A dispatching pattern might work well.

    public static class MessageDispatcher
    {
      private static readonly IMessageHandler s_DefaultHandler =
          new DefaultMessageHandler();
      private static readonly Dictionary<Type, IMessageHandler> s_Handlers =
          new Dictionary<Type, IMessageHandler>();
    
      static MessageDispatcher()
      {
        // Register a bunch of handlers.
        s_Handlers.Add(typeof(OrderMessage), new OrderMessageHandler());
        s_Handlers.Add(typeof(TradeMessage), new TradeMessageHandler());
      }
    
      public void Dispatch(IMessage msg)
      {
        Type key = msg.GetType();
        if (s_Handlers.ContainsKey(key))
        {
          // We found a specific handler! :)
          s_Handlers[key].Process(msg);
        }
        else
        {
          // We will have to resort to the default handler. :(
          s_DefaultHandler.Process(msg);
        }
      }
    }
    
    public interface IMessageHandler
    {
      void Process(IMessage msg);
    }
    
    public class OrderMessageHandler : IMessageHandler
    {
    }
    
    public class TradeMessageHandler : IMessageHandler
    {
    }
    

    There are all kinds of variations to this theme. They will all have a dispatcher object that contains many different handlers. You should consider a default handler in case the dispatcher cannot find a specific handler. There is a lot of freedom in how you choose to dispatch the messages to the appropriate handlers. I just happen to dispatch based on type, but you could make it arbitrarily more complex. Maybe the dispatcher could examine the contents of the message to discover the best handler. Maybe the message carries with it a key that identifies a preferred handler. I don't know. There are a lot of possibilities here.

    0 讨论(0)
  • 2020-11-28 03:53

    Add a ProcessMessage() method to the iMessage interface and let the concrete message polymorphically decide the right way to process themselves.

    Your code then becomes

    newMessage.ProcessMessage();
    

    Here is a good article on using polymorphism instead of conditionals.

    0 讨论(0)
  • 2020-11-28 03:54

    You might want to take a look through Enterprise Integration Patterns by Gregor Hohpe and Bobby Woolf. It has a good catalog of patterns for message processing.

    0 讨论(0)
  • 2020-11-28 03:54

    I know this is an older thread, with several very good answers over the years.

    However, in 2018, I'd use a package such as Jimmy Bogard's MediatR (https://github.com/jbogard/MediatR).

    It provides decoupling of message sending and processing with patterns such as request/response, Command/Query, One-way, Pub/Sub, async, polymorphic dispatching, etc.

    0 讨论(0)
  • 2020-11-28 03:58

    From my experience with message handling, its usually the case that different consumers of messages require handling a variety of message types. I found the Double Dispatch pattern to handle this nicely. The basic idea is to register a set of handlers that dispatch the received messages to the handler for processing based on the specific type (using function overloading). The consumers only register for the specific types they wish to receive. Below is a class diagram.

    Double Dispatch UML Class Diagram

    The code looks like this:

    IHandler

    public interface IHandler
    {
    }
    

    IMessageHandler

    public interface IMessageHandler<MessageType> : IHandler
    {
       void ProcessMessage(MessageType message);
    }
    

    IMessage

    public interface IMessage
    {
       void Dispatch(IHandler handler);
    }
    

    MessageBase

    public class MessageBase<MessageType> : IMessage
       where MessageType : class, IMessage
    {
       public void Dispatch(IHandler handler)
       {
          MessageType msg_as_msg_type = this as MessageType;
          if (msg_as_msg_type != null)
          {
             DynamicDispatch(handler, msg_as_msg_type);
          }
       }
    
       protected void DynamicDispatch(IHandler handler, MessageType self)
       {
          IMessageHandler<MessageType> handlerTarget = 
             handler as IMessageHandler<MessageType>;
          if (handlerTarget != null)
          {
             handlerTarget.ProcessMessage(self);
          }
       }
    }
    

    DerivedMessageHandlerOne

    // Consumer of DerivedMessageOne and DerivedMessageTwo 
    // (some task or process that wants to receive messages)
    public class DerivedMessageHandlerOne : 
       IMessageHandler<DerivedMessageOne>, 
       IMessageHandler<DerivedMessageTwo>
       // Just add handlers here to process incoming messages
    {     
       public DerivedMessageHandlerOne() { }
    
       #region IMessageHandler<MessaegType> Members
    
       // ************ handle both messages *************** //
       public void ProcessMessage(DerivedMessageOne message)
       {
         // Received Message one, do something with it
       }
    
       public void ProcessMessage(DerivedMessageTwo message)
       {
          // Received Message two, do something with it   
       }
    
       #endregion
    }
    

    DerivedMessageOne

    public class DerivedMessageOne : MessageBase<DerivedMessageOne>
    {
       public int MessageOneField;
    
       public DerivedMessageOne() { }
    }
    

    Then you just have a container that manages the Handlers and you are all set. A simple for loop through the list of Handlers when a message received, and the Handlers receive the messages where they want them

    // Receive some message and dispatch it to listeners
    IMessage message_received = ...
    foreach(IHandler handler in mListOfRegisteredHandlers)
    {
       message_received.Dispatch(handler);
    }
    

    This design came out of a question I asked awhile back about Polymorphic Event Handling

    0 讨论(0)
提交回复
热议问题