Какой шаблон дизайна я должен использовать здесь - PullRequest
2 голосов
/ 19 декабря 2010

В целях обучения я пишу клиент-серверное приложение на основе сокетов. Я разработал собственный протокол, чтобы гарантировать, что я могу правильно обрабатывать свои пакеты. Осматривая некоторые старые части кода сегодня, я понял, что мой способ создания пакетов данных содержит много избыточного кода.

У меня есть разные типы пакетов, например, ImagePacket, MessagePacket и т. Д. Создание всех типов отличается только незначительными элементами, например, создание заголовка и разделителя одинаково.

Чтобы улучшить это, я придумал такое решение (упрощенное):

abstract class Packet
{
 public Packet(object o)
 {
 MemoryStream memoryStream = new MemoryStream();         

 AddHeader(ref memoryStream);
 AddData(ref memoryStream, obj);
 AddDelimiter(ref memoryStream);
 _packetBytes = memoryStream.ToArray();

 memoryStream.Close();
}
protected abstract void AddData(ref MemoryStream ms, object obj);

Метод AddData реализован как абстрактный метод и переопределен в конкретных классах, тогда как AddHeader и AddDelimiter определены в самом абстрактном классе Packet.

Это прекрасно работает, и у меня не так много дублированного кода, как раньше, но я не доволен передача объекта в AddData, потому что не ясно, что я не должен давать строку конструктору ImagePacket.

// correct
Packet myMsgPacket = new MessagePacket("hello world"); 
Packet myImagePacket = new ImagePacket(image);
// wrong, but will be compiled :(
Packet myChaosPacket = new ImagePacket("muaha you're doomed");

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

Ответы [ 4 ]

3 голосов
/ 19 декабря 2010

Звучит так, как будто вам нужно использовать фабричный шаблон

Packet MyPacket = MyPacketFactory.CreatePacket(Data)

MyFactory.CreatePacket вернет IPacket

Обновление: Согласно комментарию ниже, я должен был быть более ясным. Ваша фабрика может иметь несколько перегруженных методов CreatePacket (), которые принимают разные типы данных ...

IPacket CreatePacket(Image Data) {}
IPacket CreatePacket(String Data) {}
IPacket CreatePacket(Exception Data) {}

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

IPacket CreatePacket(String Data, StringPacketTypesEnum PacketType) {}

Внутри вашей фабрики пакетов вы можете иметь общие функции, которые работают со всем дублированным кодом - например, AddDelimiter() - Это сохранит ваш код DRY

0 голосов
/ 19 декабря 2010

Я думаю, что ваше текущее решение в порядке. Вам просто нужно сузить тип данных, передаваемых конструктору ImagePacket, до типа Image. Например:

class ImagePacket : Packet{
  public ImagePacket(Image img) : base(img){}
}

Это проверит, что данные являются изображением во время копирования. Конечно, это может не сработать, если вам действительно нужно передать тип object.

0 голосов
/ 19 декабря 2010

Я бы пошел к фабричному шаблону, который возвращает интерфейс.

Кроме того, вам не нужно использовать ref для ссылочных типов.

0 голосов
/ 19 декабря 2010

Ruby on Rails использует Validations для решения этой проблемы.

Если ваша функция addData () вызвала validate () на входе, вам просто нужно переопределить validate () в каждом подклассе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...