C# 处理多种消息类型的设计模式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1477471/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Design pattern for handling multiple message types
提问by Jonathan Beerhalter
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.
我有 GOF 坐在我的桌子上,我知道一定有某种设计模式可以解决我遇到的问题,但我想不通。
For simplicities sake, I've changed the name of some of the interfaces that I'm using.
为简单起见,我更改了我正在使用的一些接口的名称。
So here's the problem, on one side of the wire, I've got multiple servers that send out different types of messages. On the other side of the wire I have a client that needs to be able to handle all the different types of messages.
所以这里的问题是,在线路的一侧,我有多个服务器发送不同类型的消息。在线路的另一端,我有一个客户端,它需要能够处理所有不同类型的消息。
All messages implement the same common interface IMessage. My problem is, when the client gets a new IMessage, how does it know what type of IMessage its received?
所有消息都实现相同的公共接口 IMessage。我的问题是,当客户端收到一个新的 IMessage 时,它如何知道它收到的是什么类型的 IMessage?
I supposed I could do something like the following, but this just FEELS awful.
我想我可以做类似以下的事情,但这感觉很糟糕。
TradeMessage tMessage = newMessage as TradeMessage;
if (tMessage != null)
{
ProcessTradeMessage(tMessage);
}
OrderMessage oMessage = newMessage as OrderMessage;
if (oMessage != null)
{
ProcessOrderMessage(oMessage);
}
The second thought, is to add a property to IMessage called MessageTypeID, but that would require me to write something like the following, which also FEELS awful.
第二个想法是向 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);
}
I know this general problem has been tackled a million times, so there has to be a nicer way of solving the problem of having a method that takes an interface as a parameter, but needs different flow control based on what class has implemented that interface.
我知道这个一般问题已经被解决了一百万次,所以必须有一个更好的方法来解决一个方法的问题,该方法将接口作为参数,但需要根据实现该接口的类进行不同的流控制。
采纳答案by Eric Petroelje
You could create separate message handlers for each message type, and naively pass the message to each available handler until you find one that can handle it. Similar to the chain of responsibility pattern:
您可以为每种消息类型创建单独的消息处理程序,并天真地将消息传递给每个可用的处理程序,直到找到可以处理它的处理程序。类似于责任链模式:
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.
}
}
}
You could also implement this as a map, with the message class name or message type id as a key and the appropriate handler instance as the value.
您还可以将其实现为映射,将消息类名称或消息类型 ID 作为键,将适当的处理程序实例作为值。
Others have suggested having the message object "handle" itself, but that just doesn't feel right to me. Seems like it would be best to separate the handling of the message from the message itself.
其他人建议让消息对象“处理”自己,但这对我来说并不合适。似乎最好将消息的处理与消息本身分开。
Some other things I like about it:
我喜欢它的其他一些事情:
You can inject the message handlers via spring or what-have-you rather than creating them in the constructor, making this very testable.
You can introduce topic-like behavior where you have multiple handlers for a single message by simply removing the "break" from the ProcessMessage loop.
By separating the message from the handler, you can have different handlers for the same message at different destinations (e.g. multiple MessageProcessor classes that handle the same messages differently)
您可以通过 spring 或 what-have-you 注入消息处理程序,而不是在构造函数中创建它们,从而使其非常易于测试。
您可以通过简单地从 ProcessMessage 循环中删除“中断”来引入类似主题的行为,其中您有单个消息的多个处理程序。
通过将消息与处理程序分开,您可以在不同的目的地为相同的消息使用不同的处理程序(例如,多个 MessageProcessor 类以不同的方式处理相同的消息)
回答by NDM
A couple of solutions are applicable for this, first is best solution, last is least best. All examples are pseudocode:
有几种解决方案适用于此,第一个是最好的解决方案,最后一个是最差的。所有示例均为伪代码:
1st, and best solution
第一,最好的解决方案
Vincent Ramdhanie introduced the actual correct pattern to solve this problem, which is called the strategy pattern.
Vincent Ramdhanie 引入了实际正确的模式来解决这个问题,称为策略模式。
This pattern creates a separate 'processor', in this case to process the messages accordingly.
这种模式创建了一个单独的“处理器”,在这种情况下相应地处理消息。
But I'm pretty sure a good explanation is given in your book by the GOF :)
但是我很确定 GOF 在您的书中给出了很好的解释 :)
2nd
第二
As commented, the message may not be able to process itself, it is still usefull to create an interface for the message, or a base class, so you can make a general processing function for a message, and overload it for more specific ones.
正如评论的那样,消息本身可能无法处理,为消息创建一个接口或基类仍然很有用,因此您可以为消息制作一个通用的处理函数,并为更具体的消息重载它。
overloading is in any case better then creating a different method for every type of message...
在任何情况下,重载都比为每种类型的消息创建不同的方法更好......
public class Message {}
public class TradeMessage extends Message {}
public class MessageProcessor {
public function process(Message msg) {
//logic
}
public function process(TradeMessage msg) {
//logic
}
}
3rd
第三名
If your message could process itself you could write an interface, since your process method depends on what message you got, it seems easier to put it inside the message class...
如果您的消息可以自行处理,您可以编写一个接口,因为您的处理方法取决于您收到的消息,将它放在消息类中似乎更容易......
public interface IMessage
{
public function process(){}
}
you then implement this in all your message classes and proccess them:
然后在所有消息类中实现它并处理它们:
list = List<IMessage>();
foreach (IMessage message in list) {
message.process();
}
in your list you can store any class that implements that interface...
在您的列表中,您可以存储实现该接口的任何类...
回答by Ryan Michela
Add a ProcessMessage() method to the iMessage interface and let the concrete message polymorphically decide the right way to process themselves.
在 iMessage 接口中添加 ProcessMessage() 方法,让具体的消息多态地决定正确的处理方式。
Your code then becomes
你的代码然后变成
newMessage.ProcessMessage();
Here is a good article on using polymorphism instead of conditionals.
这是一篇关于使用多态而不是条件的好文章。
回答by Matt Lacey
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.
所有消息都被序列化并以消息类型的标识符开始。然后我有一个 switch 语句查看标识符。然后将消息反序列化(到非常不同的对象)并进行适当的处理。
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;
...
}
}
回答by Vincent Ramdhanie
One option is to have the messages come with their own handlers. That is, create an Interface called IMessageProcessor that specifies a method processMessage(IMessage). Next define concrete class that implements IMessageProcessor for each type of message.
一种选择是让消息带有自己的处理程序。也就是说,创建一个名为 IMessageProcessor 的接口,它指定了一个方法 processMessage(IMessage)。接下来定义为每种类型的消息实现 IMessageProcessor 的具体类。
Each IMessage class will then define its own Processor.
然后每个 IMessage 类将定义自己的处理器。
When you receieve a message object you will do something like this:
当您收到消息对象时,您将执行以下操作:
message.processor.processMessage();
回答by Mike Two
You might want to take a look through Enterprise Integration Patternsby Gregor Hohpe and Bobby Woolf. It has a good catalog of patterns for message processing.
您可能需要查看Gregor Hohpe 和 Bobby Woolf 的Enterprise Integration Patterns。它有一个很好的消息处理模式目录。
回答by Hrvoje Hudo
For my little messaging framework inside Silverlight app i'm using Mediator pattern. It's some kind of messaging bus/broker, to which objects are subscribing for specific type or types of message. Then this Mediator object (broker/bus) is deciding who will receive what kind of messages.
Someting like:
对于我在 Silverlight 应用程序中的小型消息传递框架,我使用的是 Mediator 模式。它是某种消息总线/代理,对象订阅特定类型或类型的消息。然后这个 Mediator 对象(代理/总线)决定谁将接收什么样的消息。
有点像:
SubscribeFor<ChatMessage>().If(x=>x.SomeProp==true).Deliver(MyMethod);
Sample methods that are called:
调用的示例方法:
void MyMethod(ChatMessage msg) , or
void MyMethod(BaseBessage msg)
or publishing (broadcasting) of messages:
或发布(广播)消息:
Publish(new ChatMessage());
BaseMessage is abstract class, which all my messages inherits, and have just reference to sender and some unique Guid.
BaseMessage 是抽象类,我所有的消息都继承了它,并且只引用了发件人和一些唯一的 Guid。
I took starting point for building my messaging framework from MVVM Light Toolkit, you can take a look at theirs source code, it's not complicated!
我从MVVM Light Toolkit开始构建我的消息传递框架,你可以看看他们的源代码,它并不复杂!
If you whish, I can put c# code for this somewhere?
如果你愿意,我可以把 C# 代码放在某个地方吗?
回答by Brian Gideon
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.
这个主题有各种各样的变化。它们都有一个调度程序对象,其中包含许多不同的处理程序。如果调度程序找不到特定的处理程序,您应该考虑使用默认处理程序。您可以自由选择如何将消息分派给适当的处理程序。我只是碰巧根据类型进行调度,但您可以任意使其更复杂。也许调度员可以检查消息的内容以发现最佳处理程序。也许该消息带有一个标识首选处理程序的密钥。我不知道。这里有很多可能性。
回答by SwDevMan81
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 Dispatchpattern 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模式可以很好地处理这个问题。基本思想是注册一组处理程序,根据特定类型(使用函数重载)将接收到的消息分派给处理程序进行处理。消费者只注册他们希望收到的特定类型。下面是一个类图。
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
然后你只有一个管理处理程序的容器,你就全部设置好了。当收到消息时,一个简单的 for 循环遍历处理程序列表,处理程序在他们想要的地方接收消息
// 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
这个设计来自我之前问过的一个关于多态事件处理的问题
回答by Thiago Silva
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).
然而,在 2018 年,我会使用一个包,比如 Jimmy Bogard 的 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.
它通过请求/响应、命令/查询、单向、发布/订阅、异步、多态调度等模式提供消息发送和处理的解耦。