Шаблон проектирования для большого вложенного оператора switch - PullRequest
5 голосов
/ 05 июля 2011

Я искал несколько статей о рефакторинге большого оператора switch.

Но они не делают то, что я хочу.Проблема Я собираюсь перейти к , чтобы иметь гигантский оператор switch, который вызывает другой метод в зависимости от двух разных значений, скажем, type и code.

В настоящее время я бы справился со следующей ситуацией:

switch (type)
{
    case Types.Type1:
        handleType1(code);
        break;

    case Types.Type2:
        handleType2(code);
        break;
}

void handleTypeN(code)
{
    switch (code)
    {
       ...
    }
}

Может быть, что-то, что объединяет шаблон фабрики и команд, поможет мне?Я, должно быть, упускаю что-то очевидное.

Как бы вы изменили этот код?


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

Я получаю пакеты с сервера.Пакет содержит тип, код и некоторую конкретную информацию.

Как только данные поступают, я извлекаю type и code пакета, и он поступает в оператор switch дляtype, после выяснения type вызывается специальный метод для выполнения переключения на code пакета.

Метод, который обрабатывает код, теперь декодирует пакет дальше, и процесссделано.

+----------+                +----------+
|          |     Packet     |          |
|  Server  | -------------> |  Client  |
|          |                |          |
+----------+                +----------+
                                  |
                                  |
         (Switch on the type of the packet and call a specific method)
                                  |
                                  |
         (Switch on the code of the packet and call a specific method)
                                  |
                                  |
                    (Respond to the server or not)

Ответы [ 4 ]

4 голосов
/ 05 июля 2011

2 шаблон приходит на ум: команда и посетитель: http://en.wikipedia.org/wiki/Command_pattern http://en.wikipedia.org/wiki/Visitor_pattern

Abstract Class Command {

 executeSomething();

}
Class typeN extends command {

   executeSomething() {
    //...
   }
}

Class typeM extends command {

   executeSomething() {
    //...
   }
}

replace your switch by :
//my old switch
class commandManager {

processCommand() {
for listOf command in a command buffer
{ 
 onCommand(listOfcommand.command)
}
}
 onCommandPoped(command type) {
  type.executeSomething()
 }

}

you can pass parameters to executeSomething, and you can pass another command


the client code : 

{
 commandN = new CommandN()
 commandManager.postCommand( commandN)
}

После прочтения сценария использования вашего пакетного сервера, я думаю, вы можете использовать вариант шаблона стратегии http://www.oodesign.com/strategy-pattern.html где вы выбираете стратегию для вызова, когда пакет прибывает Вы можете построить это с фабрикой.

но вы не убьете свой коммутатор

Имейте в виду, что сервер может обслуживать множество клиентов. Если это, возможно, ваши случаи переключения быстрее, чем экземпляр объекта.

3 голосов
/ 05 июля 2011

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

Если у вас есть роскошь действительно вносить большие изменения в дизайн, то я бы предложил полиморфизм:

Создайте абстрактный класс пакета.
Создайте класс для каждого типа пакета.
Создайте метод фабрики, который получает необработанный серверный пакет и создает правильный объект класса пакета.
Каждый тип класса пакета будет иметь свойсобственная реализация работы, которую нужно выполнить.

Если вы не можете позволить себе большие изменения в дизайне (что часто бывает):

  • Если вы хотитеулучшить удобочитаемость:

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

  • Если вы хотите повысить производительность:

Создать матрицу, в которой для каждой ячейки [T, C] будет храниться ссылка на функцию, которая будет обрабатывать пакет с типом T и кодом C.
Матрица должна быть инициирована один раз (жестко,Обойти это невозможно) при запуске программы или класса.
Это даст вам лучшую производительность, чем длинный блок переключения (прямой доступ к коду, без логических сравнений)

2 голосов
/ 05 июля 2011

Я бы построил таблицу с типами, которая указывает на таблицы с кодами, что, в свою очередь, указывает на реализующую функцию для вызова этой пары тип / код.

lookup_type_table[type_low .. type_high] = { lookup_code_table_type_1, lookup_code_table_type_2, ...};
lookup_code_table_type_1[type_1_code_low .. type_1_code_high] = { type1_code1_func_pointer, ... };

int processPacket(int type, int code, paket_t data) {
  /* apply boundary checks on the lookup tables here! */
  return lookup_type_table[type][code](data);
}

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

Это может быть не так объектно-ориентировано, как некоторые другие шаблоны, но я исходил из C-фона :)

Наличие отображений в таблицах добавляет возможность генерировать их из некоторых других спецификаций DSL, тем самым повышая ваши DRY характеристики ...

0 голосов
/ 06 июля 2011

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

Но тогда вы замените операторы switch созданием словарей, так что в итоге разницы не будет.

Вы должны взглянуть на паттерн состояния, такого рода вещи - то, для чего он на самом деле создан.

...