Гибкая конструкция для замены оператора - PullRequest
4 голосов
/ 15 июня 2011

Я работаю над сетевой программой и проектирую сервер Linux с использованием C ++.Это довольно просто для разработки базовой структуры.У меня есть определение пакета с заголовком, который имеет фиксированный размер.

typedef enum{
     PACKET_LOGIN_REQ = 1,
     PACKET_LOGIN_RES,
     PACKET_STORE_REQ,
     PACKET_STORE_RES
}PACKET_TYPES;

typedef struct {
     PACKET_TYPES type;
     short bodySize,
     long long deviceId
}HEADER;

.
.

/*more definitions here*/

typedef struct{
     HEADER head;
     union BODY{
          LOGIN_REQ loginReq;
          LOGIN_RES loginRes;
          .
          .
          more types
     }
}

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

Я использую тип объединения, поэтому мне не нужно изменять всю структуру пакета.Вместо этого я могу добавить недавно добавленные типы пакетов в структуру объединения.

Однако, когда я пытаюсь проанализировать необработанные данные для помещения в пакет, используя оператор switch, я должен каждый раз добавлять каждый оператор switch.

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

Есть ли лучший способ справиться с этим (лучший шаблон проектирования)?А как насчет связанных учебников или ссылок?

Ответы [ 4 ]

7 голосов
/ 15 июня 2011

Вы должны использовать классы и вместо switch использовать полиморфный вызов.

Нечто подобное (псевдокодиш):

class Packet{
public:
   Packet();
   virtual ~Packet();
   virtual HandleMe()=0;
protected:
   // fields 
}

PacketTypeOne: public Packet{
public:
  virtual HandleMe(); // implementation for type 1
}

PacketType2: public Packet{
public:
  virtual HandleMe(); // implementation for type 2
}

PacketTypeX: public Packet{
public:
  virtual HandleMe(); // implementation for type X
}

и где вы получаете пакеты:

...
Packet* newPacket = PacketFactory::CreateNewPacket(packetTypeX); // factory will create
              // the right instance.
...
// now handle it:
newPacket->HandleMe();
...

Готово.

3 голосов
/ 15 июня 2011

Поскольку ваш код выполнен в стиле C, а не в стиле C ++, я дам вам ответ на языке C.

Используйте функциональные указатели. Что-то вроде:

typedef int (*ParseFunc)(const char *, len, struct MyStruct);

typedef struct PacketParser_
{
    PACKETType type;
    ParseFunc parseFunc;
} PacketParser;

const PacketParser[] parser = 
{
   { PACKET_LOGIN_REQ, LoginReqParser }
   { PACKET_LOGIN_RES, LoginResParser }
   .
   .
   .
}

Затем вы перебираете все типы пакетов и вызываете соответствующую функцию. Всякий раз, когда вы получаете новый тип пакета, вы просто добавляете еще одну строку в вашу PacketParser функцию.

Если вы используете C ++, вам лучше использовать полиморфизм.

1 голос
/ 15 июня 2011

В большинстве случаев вам следует избегать union с.Это один из таких случаев: -)

Ответ, как уже отмечалось, заключается в использовании полиморфизма.Канонический способ преобразования операторов switch в полиморфизм - использование шаблона проектирования Visitor .

. В то время как обычный полиморфизм с использованием virtual методов может помочьпроблема.Методы virtual обеспечивают отправку правильного метода на основе одного объекта (Packet).Но вы также зависите от того, какие действия предпринять.Например, если у вас много операторов switch, а не одного, тогда предпочтительным будет шаблон Visitor.

0 голосов
/ 15 июня 2011

Возможно, вы захотите ...

По сути, вы найдете хороший суперкласс, и для каждогоtypecode создает отдельный подкласс.

Вы также хотите купить копию «Рефакторинг: улучшение дизайна существующего кода»., Fowler, Martin (1999) / Addison Wesley. .

...