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作为键,并将相应的处理程序实例作为值.
其他人建议让消息对象本身"处理",但这对我来说感觉不对.似乎最好将消息的处理与消息本身分开.
我喜欢的其他一些事情:
你可以通过spring或者what-have-you注入消息处理程序,而不是在构造函数中创建它们,这使得它非常容易测试.
通过简单地从ProcessMessage循环中删除"break",您可以引入类似于主题的行为,其中您可以为单个消息提供多个处理程序.
通过将消息与处理程序分离,您可以在不同的目标处为同一消息提供不同的处理程序(例如,以不同方式处理相同消息的多个MessageProcessor类)
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)
在您的列表中,您可以存储任何实现该接口的类...
根据我在消息处理方面的经验,通常情况是消息的不同消费者需要处理各种消息类型.我找到了Double Dispatch模式来很好地处理这个问题.基本思想是注册一组处理程序,这些处理程序将接收到的消息分派给处理程序,以便根据特定类型进行处理(使用函数重载).消费者只注册他们希望收到的特定类型.下面是一个类图.

代码如下所示:
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)
这个设计出自一个我回答多态事件处理的问题