Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Design pattern for a large nested switch statements

I've searched for a number of articles on refactoring a large switch statement.

But they don't do what I want to do. The problem I'm going to to run in to is having a gigantic switch statement which calls a different method depending on two different values, lets say a type and a code.

Currently, I would handle the situation like this:

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

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

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

Maybe something which combines the factory and command pattern would help me out? I must be missing something obvious.

How would you refactor this code?


I might need to get a little more specific as to what scenario I'm facing.

I'm receiving packets from a server. A packet contains a type and a code and some specific information.

As soon as data arrives I retrieve the type and the code of the packet and it goes in to the switch statement for the type, after figuring out the type a specific method is called to perform a switch on the code of the packet.

The method that handles the code now decodes the packet further and the process is done.

+----------+                +----------+
|          |     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)
like image 820
Kevin Avatar asked Jul 05 '11 10:07

Kevin


3 Answers

2 pattern comes in mind : command and visitor : 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)
}

After reading your packet server use case, I think you can use a variant of strategy pattern http://www.oodesign.com/strategy-pattern.html where you choose the strategy to call when the packet arrive you can build this with a factory.

but you will not kill your switch case

Keep in mind, that a server can serve many client. If it's maybe your switch cases are faster than object instanciation.

like image 163
Damien MIRAS Avatar answered Oct 15 '22 02:10

Damien MIRAS


I think it depends what kind of code improvement you're trying to do.

If you have the luxury of actually making big design changes, then I'd suggest polymorphism:

Create an abstract Packet class.
Create a class for each packet type.
Create a factory method, that receives a raw server packet, and creates the right packet class object.
Each packet class type will have its own implementation of the job it needs to do.

If you don't have the luxury of doing large design changes (which is often the case):

  • If you want to improve readability :

Keep the switch, each switch case will call a properly named function that will do what it needs to.

  • If you want to increase performance:

Create a matrix, that for each cell [T,C] will hold a reference to a function that will handle a Packet with Type T and Code C.
The matrix should be initiated once (hard-coded, no way around that) at startup of program or class.
This will give you better performance than a long switch block (direct access to code, no logical comparisons)

like image 3
Yochai Timmer Avatar answered Oct 15 '22 02:10

Yochai Timmer


I'd build a table with types which points to tables with codes, which in turn points to the implementing function to call for that type/code pair.

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);
}

The tables could be implemented as linked lists, or with some other fancier container implementation to make them more dynamic, if needed.

This might not be as object-oriented as some other patterns, but I'm coming from a C-background :)

Having the mappings in tables adds the possibility to generate them from some other DSL spec though, boosting your DRY stats...

like image 2
Kaos Avatar answered Oct 15 '22 01:10

Kaos