7

我搜索了许多关于重构大型switch语句的文章。

但他们不做我想做的事。我要遇到的问题是有一个巨大的 switch 语句,它根据两个不同的值调用不同的方法,比如说 atype和 a code

目前,我会处理这样的情况:

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

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

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

也许结合工厂和命令模式的东西会帮助我?我一定遗漏了一些明显的东西。

你将如何重构这段代码?


我可能需要更具体地了解我所面临的情况。

我正在从服务器接收数据包。一个数据包包含一个类型和一个代码以及一些特定信息。

一旦数据到达,我就检索数据包的typecode,然后它进入 的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 回答 4

5

想到 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 在其中选择在数据包到达时调用的策略,您可以使用工厂。

但你不会杀死你的开关盒

请记住,服务器可以为许多客户端提供服务。如果是这样,您的切换案例可能比对象实例化更快。

于 2011-07-05T11:09:34.480 回答
3

我认为这取决于您要尝试进行哪种代码改进。

如果您有实际进行重大设计更改的奢侈,那么我建议使用多态性:

创建一个抽象的 Packet 类。
为每种数据包类型创建一个类。
创建一个工厂方法,它接收原始服务器数据包,并创建正确的数据包类对象。
每个数据包类类型将有自己的实现它需要做的工作。

如果您没有进行大型设计更改的奢侈(通常是这种情况):

  • 如果你想提高可读性:

保留开关,每个开关案例都会调用一个正确命名的函数,该函数将完成它需要做的事情。

  • 如果你想提高性能:

创建一个矩阵,对于每个单元格 [T,C] 将保存对一个函数的引用,该函数将处理类型为 T 和代码 C 的数据包。
该矩阵应在启动时启动一次(硬编码,无法解决)程序或类。
这将为您提供比长开关块更好的性能(直接访问代码,没有逻辑比较)

于 2011-07-05T12:48:32.603 回答
2

我会建立一个类型表,该表指向带有代码的表,而代码又指向实现函数以调用该类型/代码对。

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统计数据......

于 2011-07-05T11:20:35.440 回答
0

您可以将您的开关替换为字典字典,该字典指向实际方法或更好的类来处理数据包。第一个字典使用类型作为索引,第二个使用代码作为索引。

但是随后您将通过创建字典来替换 switch 语句,因此最终没有太大区别。

你也应该看看状态模式,这种东西就是它的真正用途。

于 2011-07-06T07:08:53.023 回答