Каков правильный шаблон проектирования C # для нескольких методов, которые все принимают как параметры, отличающиеся от производных классов? - PullRequest
2 голосов
/ 15 августа 2010

У меня есть базовый класс:

class Message

И два производных класса:

class SimpleMessage : Message
class ComplexMesssage : Message

Эти типы используются в другой части кода как таковые:

void ProcessSimpleMessage(SimpleMessage m)
void ProcessComplexMessage(ComplexMessage m)

Эти методы не находятся внутри класса Message, так как обработка не является частью сообщения.

Теперь я хотел бы избежать структуры if / else / switch, поскольку существует много типовСообщения.Какой шаблон дизайна лучше всего использовать здесь?

Одним из вариантов является инкапсуляция с использованием шаблона стратегии (по крайней мере, насколько я понимаю):

class ProcessableMessage
{
delegate void ProcessMessageDelegate(Message m)

private Message m;
private ProcessMessageDelegate ProcessMessage;
}

Но действительно ли лучше, чтобы все методы обработки принимали базовый тип Сообщениеи бросить внутрь?И еще одна проблема может заключаться в том, что динамический тип сообщения (простой или сложный) на самом деле хранится в 2 местах в этом классе - сообщение и алгоритм процесса, что выглядит некрасиво.

Любойлучшие решения там?

Спасибо !!
Ассаф

Ответы [ 3 ]

4 голосов
/ 15 августа 2010

Я бы использовал здесь шаблон посетителя:

public interface IMessageVisitor
{
    void VisitSimple(SimpleMessage msg);
    void VisitComplex(ComplexMessage msg);
}

public abstract class Message
{
    public abstract void Accept(IMessageVisitor visitor);
}

public class SimpleMessage : Message
{
    public override void Accept(IMessageVisitor visitor)
    {
        visitor.VisitSimple(this);
    }
}

public class ComplexMessage : Message
{
    public override void Accept(IMessageVisitor visitor)
    {
        visitor.VisitComplex(this);
    }
}

public class MessageProcessor : IMessageVisitor
{
    void IMessageVisitor.VisitSimple(SimpleMessage msg)
    { process simple message }

    void IMessageVisitor.VisitComplex(ComplexMessage msg)
    { process complex message }

    public void Process(Message msg)
    {
        msg.Accept(this);
    }
}
3 голосов
/ 15 августа 2010

Почему бы просто не добавить виртуальный метод:

class Message
{
    public abstract void Process();
}

Если вам действительно нужно разделить код:

class Message
{
    public abstract void Process();
}

class SimpleMessage
{
    public override void Process()
    {
        new SimpleMessageProcessor().Process();
    }
}

class SimpleMessageProcessor
{
    internal void Process()
    {
        // ...
    }
}

Я имею в виду, какая гибкость нам нужна здесь?Какие другие классы участвуют?Какой у вас сценарий?Без какого-либо другого контекста, это действительно самый простой для понимания и самый простой в реализации метод.Иногда люди добавляют дизайн, когда он действительно не нужен.

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

0 голосов
/ 15 августа 2010

Мне нравится подход посетителя выше. Однако просто для удовольствия я покажу, как уменьшить избыточность в коде, используя T4 в VS2008 и VS2010.

Избыточность исходит из того, что для каждого сообщения вам нужен метод Visit. Также каждому методу нужна простая, но избыточная реализация Accept. Один из способов приблизиться к «Не повторяйся» - это сгенерировать код, используя T4.

Чтобы протестировать следующий пример, добавьте класс в VS, но измените расширение с .cs на .tt. Теперь вы получите два файла .tt-файл и .cs-файл, подключенные к .tt-файлу.

Файл .tt - это шаблон, который генерирует файл .cs. В то время они идентичны.

Используйте это как содержимое файла .tt:

<#@ template language="C#" #>
<#
   // On VS2008 change C# above to C#v3.5

   // -----------------------------------------------------
   // Here we declare our different message types
   var messageTypes = new []
      {
         "Simple",
         "Complex",
         "Other",
      };
   // -----------------------------------------------------
#>

namespace MessageProcessor
{
   partial interface IMessageVisitor
   {
<#
   // Let's generate all message visitor methods
   foreach (var messageType in messageTypes)
   {
#>
      void Visit (<#=messageType#>Message message);
<#
   }
#>
   }
   abstract partial class Message
   {      
      public abstract void Accept (IMessageVisitor visitor);
   }
<#
   // Let's generate all message types
   foreach (var messageType in messageTypes)
   {
#>
   sealed partial class <#=messageType#>Message : Message
   {      
      public override void Accept (IMessageVisitor visitor)
      {
         visitor.Visit (this);
      }
   }
<#
   }
#>
}

Это должно сгенерировать CS-файл, который выглядит следующим образом:

namespace MessageProcessor
{
   partial interface IMessageVisitor
   {
      void Visit (SimpleMessage message);
      void Visit (ComplexMessage message);
      void Visit (OtherMessage message);
   }
   abstract partial class Message
   {      
      public abstract void Accept (IMessageVisitor visitor);
   }
   sealed partial class SimpleMessage : Message
   {      
      public override void Accept (IMessageVisitor visitor)
      {
         visitor.Visit (this);
      }
   }
   sealed partial class ComplexMessage : Message
   {      
      public override void Accept (IMessageVisitor visitor)
      {
         visitor.Visit (this);
      }
   }
   sealed partial class OtherMessage : Message
   {      
      public override void Accept (IMessageVisitor visitor)
      {
         visitor.Visit (this);
      }
   }
}

Почему это менее избыточно? Потому что теперь, когда мне нравится добавлять новое сообщение, я просто добавляю его в шаблон:

   var messageTypes = new []
      {
         "Simple",
         "Complex",
         "Other",
         "YetAnotherOne",
      };

Важно отметить, что все сообщения генерируются как частичные, потому что нам нужны разные полезные нагрузки для сообщения. Это указано в другом файле и может выглядеть так:

   partial class SimpleMessage
   {
      public string Name;
   }

   partial class ComplexMessage
   {
      public XmlDocument Xml;
   }

Для тех, кто любит звук T4, просмотрите этот блог: http://www.olegsych.com/2008/09/t4-tutorial-creatating-your-first-code-generator/

...