用于处理多种消息类型的设计模式

Jon*_*ter 34 c# design-patterns

我已经把GOF放在我的桌子上了,我知道必须有某种设计模式来解决我遇到的问题,但是我无法弄明白.

为了简单起见,我改变了我正在使用的一些接口的名称.

所以这就是问题,在线路的一侧,我有多个服务器发送不同类型的消息.在电线的另一端,我有一个客户端,需要能够处理所有不同类型的消息.

所有消息都实现相同的公共接口IMessage.我的问题是,当客户端获得新的IMessage时,它如何知道它收到的IMessage类型?

我想我可以做类似下面的事情,但这只是感觉太糟糕了.

TradeMessage tMessage = newMessage as TradeMessage;
if (tMessage != null)
{
    ProcessTradeMessage(tMessage);
}

OrderMessage oMessage = newMessage as OrderMessage;
if (oMessage != null)
{
    ProcessOrderMessage(oMessage);
}
Run Code Online (Sandbox Code Playgroud)

第二个想法是向IMessage添加一个名为MessageTypeID的属性,但是这需要我写下面的内容,这也很糟糕.

TradeMessage tMessage = new TradeMessage();
if (newMessage.MessageTypeID == tMessage.MessageTypeID)
{
    tMessage = newMessage as TradeMessage;
    ProcessTradeMessage(tMessage); 
}

OrderMessage oMessage = new OrderMessage();
if (newMessage.MessageTypeID == oMessage.MessageTypeID)
{
    oMessage = newMessage as OrderMessage;
    ProcessOrderMessage(oMessage);
}
Run Code Online (Sandbox Code Playgroud)

我知道这个一般问题已被解决了一百万次,所以必须有一种更好的方法来解决一个方法,该方法将接口作为参数,但需要基于哪个类实现该接口的不同流控制.

Eri*_*lje 22

您可以为每种消息类型创建单独的消息处理程序,并将消息天真地传递给每个可用的处理程序,直到找到可以处理它的消息处理程序.与责任链模式类似:

public interface IMessageHandler {
    bool HandleMessage( IMessage msg );
}

public class OrderMessageHandler : IMessageHandler {
    bool HandleMessage( IMessage msg ) {
       if ( !(msg is OrderMessage)) return false;

       // Handle the message and return true to indicate it was handled
       return true; 
    }
}

public class SomeOtherMessageHandler : IMessageHandler {
    bool HandleMessage( IMessage msg ) {
       if ( !(msg is SomeOtherMessage) ) return false;

       // Handle the message and return true to indicate it was handled
       return true;
    }
}

... etc ...

public class MessageProcessor {
    private List<IMessageHandler> handlers;

    public MessageProcessor() {
       handlers = new List<IMessageHandler>();
       handlers.add(new SomeOtherMessageHandler());
       handlers.add(new OrderMessageHandler());
    }

    public void ProcessMessage( IMessage msg ) {
       bool messageWasHandled
       foreach( IMessageHandler handler in handlers ) {
           if ( handler.HandleMessage(msg) ) {
               messageWasHandled = true;
               break;
           }
       }

       if ( !messageWasHandled ) {
          // Do some default processing, throw error, whatever.
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

您还可以将其实现为映射,将消息类名称或消息类型id作为键,并将相应的处理程序实例作为值.

其他人建议让消息对象本身"处理",但这对我来说感觉不对.似乎最好将消息的处理与消息本身分开.

我喜欢的其他一些事情:

  1. 你可以通过spring或者what-have-you注入消息处理程序,而不是在构造函数中创建它们,这使得它非常容易测试.

  2. 通过简单地从ProcessMessage循环中删除"break",您可以引入类似于主题的行为,其中您可以为单个消息提供多个处理程序.

  3. 通过将消息与处理程序分离,您可以在不同的目标处为同一消息提供不同的处理程序(例如,以不同方式处理相同消息的多个MessageProcessor类)

  • if(!msg是SomeOtherMessage)应该是if(!(msg是SomeOtherMessage))。`OrderMessageHandler`和`SomeOtherMessageHandler`应该实现`IMessageHandler`,以便能够将它们添加到`List &lt;IMessageHandler&gt;处理程序中。同样,“ bool messageWasHandled”也会丢失“;”。(您真的测试过吗?) (2认同)

NDM*_*NDM 15

一些解决方案适用于此,首先是最佳解决方案,最后是最不好的解决方案.所有示例都是伪代码:

第1,最好的解决方案

Vincent Ramdhanie介绍了解决这个问题的实际正确模式,称为策略模式.

此模式创建一个单独的"处理器",在这种情况下,相应地处理消息.

但我很确定GOF的书中给出了一个很好的解释:)

第2

如评论所述,消息可能无法自行处理,为消息或基类创建接口仍然很有用,因此您可以为消息创建一般处理函数,并为更具体的消息重载消息.

在任何情况下,重载都比为每种类型的消息创建不同的方法更好......

public class Message {}
public class TradeMessage extends Message {}

public class MessageProcessor {
    public function process(Message msg) {
        //logic
    }

    public function process(TradeMessage msg) {
        //logic
    }
}
Run Code Online (Sandbox Code Playgroud)

第3

如果您的消息可以处理自己,您可以编写一个接口,因为您的处理方法取决于您获得的消息,将它放在消息类中似乎更容易...

public interface IMessage
{
    public function process(){}
}
Run Code Online (Sandbox Code Playgroud)

然后在所有消息类中实现它并处理它们:

list = List<IMessage>();
foreach (IMessage message in list) {
    message.process();
}
Run Code Online (Sandbox Code Playgroud)

在您的列表中,您可以存储任何实现该接口的类...


SwD*_*n81 8

根据我在消息处理方面的经验,通常情况是消息的不同消费者需要处理各种消息类型.我找到了Double Dispatch模式来很好地处理这个问题.基本思想是注册一组处理程序,这些处理程序将接收到的消息分派给处理程序,以便根据特定类型进行处理(使用函数重载).消费者只注册他们希望收到的特定类型.下面是一个类图.

Double Dispatch UML类图

代码如下所示:

IHandler

public interface IHandler
{
}
Run Code Online (Sandbox Code Playgroud)

IMessageHandler

public interface IMessageHandler<MessageType> : IHandler
{
   void ProcessMessage(MessageType message);
}
Run Code Online (Sandbox Code Playgroud)

即时聊天

public interface IMessage
{
   void Dispatch(IHandler handler);
}
Run Code Online (Sandbox Code Playgroud)

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);
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

DerivedMessageOne

public class DerivedMessageOne : MessageBase<DerivedMessageOne>
{
   public int MessageOneField;

   public DerivedMessageOne() { }
}
Run Code Online (Sandbox Code Playgroud)

然后你就有了一个管理处理程序的容器,你就完成了.收到消息时,处理程序列表的简单循环,以及处理程序接收他们想要的消息

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

这个设计出自一个我回答多态事件处理的问题