大型嵌套switch语句的设计模式

时间:2011-07-05 10:42:14

标签: oop design-patterns polymorphism switch-statement

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

但他们没有做我想做的事。 我要运行的问题是有一个巨大的switch语句,根据两个不同的值调用不同的方法,让我们说typecode

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

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 个答案:

答案 0 :(得分:4)

记住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 您选择在数据包到达时调用的策略 你可以用工厂来建造它。

但你不会杀掉你的开关案例

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

答案 1 :(得分:3)

我认为这取决于您尝试做什么样的代码改进。

如果您真的能够进行大的设计更改,那么我建议使用多态:

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

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

  • 如果您想提高可读性:

保持开关,每个开关盒都会调用一个正确命名的功能,该功能将完成所需的功能。

  • 如果您想提高效果:

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

答案 2 :(得分: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-background:)

在表格中添加映射会增加从其他DSL规范生成它们的可能性,从而提升您的DRY统计数据......

答案 3 :(得分:0)

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

但是你会通过创建字典替换switch语句,所以最后没有太大的区别。

你也应该看一下状态模式,这种东西就是它实际制作的东西。