Шаблон проектирования для загрузки нескольких типов сообщений - PullRequest
3 голосов
/ 16 июня 2010

Просматривая SO, я столкнулся с вопросом о обработке нескольких типов сообщений . Меня беспокоит - как мне аккуратно загрузить такое сообщение? Я решил иметь отдельный класс с методом, который загружает одно сообщение каждый раз, когда оно вызывается. Этот метод должен создать новый экземпляр конкретного типа сообщения (скажем, 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);
// ...

На самом деле, если весь дизайн выглядит слишком грязно и у вас, ребята, есть лучшее решение, я готов реструктурировать все это.

Если мое описание слишком хаотично, пожалуйста, дайте мне знать, чего не хватает, и я отредактирую вопрос. Спасибо всем.

Изменить: Что мне не нравится в этом коде, так это то, что я должен создать экземпляр сообщения (потому что он знает, как загрузить себя), а затем должен украсить его конкретным типом сообщения (потому что декораторы знают, как интерпретировать свойство Data msg. ). Может быть, это сделает вопрос немного более ясным.

Ответы [ 4 ]

3 голосов
/ 16 июня 2010

Я согласен с CkH в том, что шаблон Factory будет решать эту проблему.Я написал глупый пример в качестве доказательства концепции.Не для того, чтобы показать хороший дизайн класса, просто работает простой шаблон Factory.Даже если вы используете несколько типов сообщений и обработчиков, вам нужно лишь слегка изменить этот шаблон.

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 голос
/ 16 июня 2010

Следующий уровень абстракции - сделать обнаружение и создание сообщений динамическими.Это часто достигается путем привязки имени строки к каждому сообщению или использования имени класса в качестве идентификатора.Вы можете использовать Reflection для обнаружения доступных типов сообщений, сохранять их в словаре и предоставлять экземпляры по имени.Это может быть дополнительно расширено, чтобы вводить сообщения из динамически загружаемых сборочных модулей с соответствующими метаданными и интерфейсами, чтобы обеспечить слабосвязанную композицию между различными сообщениями и потребителями сообщений.Когда вы дойдете до этого уровня, я рекомендую изучить такие структуры, как MEF , которые автоматизируют процесс обнаружения и внедрения экземпляра.

Для вашего простого приложения, я думаю, ваш подход уже достаточно чист.Серия операторов if или переключение работает просто отлично, и их легко понять / поддерживать, если у вас относительно небольшой и стабильный набор случаев.


Подводя итоги дальнейшего обсуждения в комментариях:

Основной проблемой при создании проблемы было то, что различные конкретные сообщения, унаследованные от Message, и, тем не менее, базовое Сообщение необходимо было создать, прежде чем более конкретные сообщения могли выполнить дальнейший анализ.Это неясно, предназначено ли Сообщение для того, чтобы содержать необработанную информацию или действовать как базовый тип для интерпретируемых сообщений.Лучшим дизайном является разделение функциональности RawMessage на собственный класс, четко разделяющий проблемы и устраняющий ощущение «создания экземпляра дважды».

Что касается рефакторинга с DTO и классом сопоставления:

Iна самом деле предпочитают ваш подход для кодирования / декодирования сообщений для конкретного приложения.Если я хочу отследить, почему FactoryTakenOverByRobotsMessage содержит недопустимые данные, для меня имеет смысл, что метод парсера для сообщения содержится в декодированных данных для сообщения.Где все становится более рискованным, если вы хотите поддерживать разные кодировки, как сейчас, вы начинаете хотеть декларативно указывать DTO (например, с атрибутами) и разрешать вашим различным транспортным уровням решать, как сериализовать / десериализовать.Тем не менее, в большинстве случаев, когда я использую ваш шаблон, это для конкретного приложения, часто с несовместимыми форматами сообщений и различными проприетарными кодировками, которые не отображаются должным образом каким-либо автоматическим способом.Я всегда могу использовать декларативное кодирование параллельно с проприетарной кодировкой в ​​своем классе и выполнять сериализацию своих сообщений в XML для целей отладки.

0 голосов
/ 16 июня 2010

То, что у тебя, выглядит отлично. Это однозначно. Если ваши объекты AlphaMessage и BetaMessage являются дочерними объектами Message, то вместо создания нового объекта просто верните приведенный объект.

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

Потребительский код должен обрабатывать случай, когда приведение не удается (, когда возвращает ноль).

0 голосов
/ 16 июня 2010

С C # вам, вероятно, понадобится что-то наподобие того, что вы написали, потому что C # - язык со строгой типизацией.По сути, вы должны получить конкретные классы где-то в вашем коде.

...