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)
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.
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):
Keep the switch, each switch case will call a properly named function that will do what it needs to.
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)
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With