用于加载多种消息类型的设计模式

时间:2010-06-16 19:17:44

标签: c# .net design-patterns architecture

正如我所看到的那样,我遇到了一个关于handling multiple message types的问题。我担心的是 - 如何以整洁的方式加载这样的消息?我决定让一个单独的类使用一个方法,每次调用时都会加载一条消息。此方法应创建具体消息类型的新实例(例如AlphaMessage,BetaMessage,GammaMessage等)并将其作为消息返回。

class MessageLoader
{
    public Message Load()
    {
        // ...
    }
}

方法中的代码对我来说非常糟糕,我非常想重构它/摆脱它:

Message msg = Message.Load(...); // load yourself from whatever source
if (msg.Type == MessageType.Alpha) return new AlphaMessage(msg);
if (msg.Type == MessageType.Beta) return new BetaMessage(msg);
// ...

事实上,如果整个设计看起来太乱,你们有一个更好的解决方案,我已经准备好重组整个事情了。

如果我的描述过于混乱,请告诉我它缺少什么,我将编辑问题。谢谢大家。

修改 我不喜欢这段代码的是我必须创建一个Message实例(因为它知道如何加载自己)然后必须用具体的消息类型来装饰它(导致装饰器知道如何解释msg的Data属性) )。也许这会使问题更加明确。

4 个答案:

答案 0 :(得分:3)

我同意CkH的意见,工厂模式将解决它。我写了一个愚蠢的例子作为概念证明。并不意味着展示良好的类设计,只是简单的工厂模式工作。即使您使用多种消息类型和处理程序,也只需稍微修改此模式即可。

class Class12
{
    public static void Main()
    {
        Message m = new Message(1, "Hello world");
        IMessageHandler msgHandler = Factory.GetMessageHandler(m.MessageType);
        msgHandler.HandleMessage(m);

        Message m2 = new Message(2, "Adios world");
        IMessageHandler msgHandler2 = Factory.GetMessageHandler(m2.MessageType);
        msgHandler2.HandleMessage(m2);
    }
}
public class Factory
{
    public static IMessageHandler GetMessageHandler(int msgType)
    {
        IMessageHandler msgHandler = null;
        switch(msgType)
        {
            case 1: 
                msgHandler = new MessageHandler1();
                break;
            case 2: 
                msgHandler = new MessageHandler2();
                break;
            default: 
                msgHandler = new MessageHandler1();
                break;
        }
        return msgHandler;
    }
}
public class Message
{
    public int MessageType { get; set; }
    public string AMessage { get; set; }

    public Message(int messageType, string message)
    {
        this.MessageType = messageType;
        this.AMessage = message;
    }
}
public interface IMessageHandler
{
    void HandleMessage(Message m);
}
class MessageHandler1 : IMessageHandler
{

    #region IMessageHandler Members


    public void HandleMessage(Message m)
    {
        string message = m.AMessage;
        Console.WriteLine(message);
    }

    #endregion
}
class MessageHandler2 : IMessageHandler
{

    #region IMessageHandler Members


    public void HandleMessage(Message m)
    {
        string message = m.AMessage;
        Console.WriteLine("Hey there " + message);
    }

    #endregion
}

答案 1 :(得分:1)

下一级抽象是使消息发现和实例化动态化。这通常通过将字符串名称与每个Message关联或使用类的名称作为标识符来实现。您可以使用Reflection来发现可用的Message类型,将它们存储在Dictionary中并按名称提供实例化。这可以进一步扩展为从动态加载的“插件”程序集中引入消息,并使用适当的元数据和接口,以允许不同消息和消息使用者之间松散耦合的组合。一旦达到该级别,我建议调查像MEF这样的框架,它可以自动执行发现和实例注入过程。

对于您的简单应用,我认为您的方法已经相当干净。只要您拥有相对较小且稳定的案例集,一系列if语句或开关就可以正常工作并且易于理解/维护。


总结评论中的进一步讨论:

创建不安的主要设计问题是,在更具体的消息可以执行进一步分析之前,必须实例化从Message继承的不同特定消息以及基本消息。这使得消息旨在包含原始信息或充当解释消息的基本类型变得混乱。更好的设计是将RawMessage功能分离到自己的类中,明确区分关注点并解决“实例化两次”的感觉。

至于使用DTO和mapper类进行重构:

我实际上更喜欢您的方法来进行特定于应用的消息编码/解码。如果我想跟踪FactoryTakenOverByRobotsMessage为什么包含无效数据,那么对我来说,消息的解析器方法包含在消息的解码数据中是有意义的。如果您想要支持不同的编码,那么事情会变得更加冒险,因为现在您开始想要以声明方式指定DTO(例如使用属性)并允许您的不同传输层决定如何序列化/反序列化。然而,在我使用您的模式的大多数情况下,它是针对特定于应用程序的情况,通常有些不一致的消息格式和各种专有编码,这些编码无法以任何自动方式很好地映射。我总是可以使用声明性编码与专有的类内编码并行执行,例如将我的消息序列化为XML以进行调试。

答案 2 :(得分:0)

使用C#,您可能需要类似于您编写的内容,因为C#是一种强类型语言。基本上,你必须在代码中的某个地方获得具体的类。

答案 3 :(得分:0)

你看起来很好。这是毫不含糊的。如果您的AlphaMessage和BetaMessage对象是Message的子对象,那么只需返回已铸造的对象,而不是创建新对象。

if (msg.Type == MessageType.Alpha) return msg as AlphaMessage;
else if (msg.Type == MessageType.Beta) return msg as BetaMessage;

消费者代码必须处理强制转换失败的情况(返回null)。