用HashMap替换switch语句

时间:2013-05-01 13:16:09

标签: java hashmap

我有一个接收包(字节数组)的套接字类。每个包都有一个整数,以便识别其类型。 switch语句中的每个case都是这样的:

switch(packet.getHeader().getID()) {
case PingRequest.ID:
    if(data.length != PingRequest.SIZE)
        return null;

    PingRequest pingRequest = new PingRequest(data); /* specific parsing of the byte array */
    pingRequest.setHeader(data); /* method of the abstract class */
    packet = pingRequest;
break;
case Error.ID:
    if(data.length != Error.SIZE)
        return null;

    Error error = new Error(data);
    error.setHeader(data);
    packet = error;

    break;
...

每个数据包都有不同的信息,这就是为什么每个数据包都有不同的构造函数(从字节数组data创建数据包成员)

由于每个案例看起来都有些相似(并且有很多案例)我认为我应该使用HashMap来优化它:

public static HashMap<Integer, Class<?>> _packetTypes = new HashMap<Integer, Class<?>>();
_packetTypes.put(PingRequest.ID, PingRequest.class);

我现在想要达到的目标是:

Class<?> myClass = PacketAbstract._packetTypes.get(packet.getHeader().getID());

Class[] cArg = new Class[1];
cArg[0] = Byte.class;
Constructor<?> ct = myClass.getConstructor(cArg);

/* Doesn't make any sense from here on */
myClass specialPacket = ct.newInstance(data); 
specialPacket.setHeader(data);
packet = specialPacket;

因此,基本思想是创建一个包含数据包ID和相应数据包类的哈希映射,这将允许我创建专用数据包。最后一个代码部分用于替换我的switch语句。

问题:

我的想法是如何解决这个问题的?如果不是请赐教。 我如何实现我的代码的最后一部分到目前为止没有意义(如果这是正确的方法)?

编辑:数据包是抽象类Packet的一个对象,它被PingRequest

等每个特殊数据包扩展。

3 个答案:

答案 0 :(得分:1)

看起来你的想法会起作用。我认为代码需要是这样的:

Class<? extends Packet> myClass = PacketAbstract._packetTypes.get(packet.getHeader().getID());

Constructor<Packet> ct = myClass.getConstructor(data.getClass());
Packet specialPacket = ct.newInstance(data);
specialPacket.setHeader(data);
packet = specialPacket;

然而,在我看来,使用反射通常是最后的手段。考虑查看其他模式,例如可能有助于此类问题的Visitor Pattern

另一种选择是在枚举中嵌入每个标识符的正确具体Class,如下所示:

enum PacketID {
    private int id;
    private int size;
    private Class<? extends Packet> subClass;
    private PacketID(int id, int size, Class<? extends Packet> subClass) {
        this.id = id;
        this.size = size;
        this.subClass = subClass;
    }

    ERROR(  1, 100, Error.class),
    PINGREQ(2,  25, PingRequest.class);
}

这也可以让你抽象出我在第一个HashMap解决方案中没有看到的尺寸检查,并且可能完全跳过HashMap

编辑:将参数更改为getConstructor(),因为它似乎使用字节数组而不仅仅是Byte。 编辑:使用枚举字段添加第二个备用解决方案。

答案 1 :(得分:0)

Class<?> myClass = PacketAbstract._packetTypes.get(packet.getHeader().getID());

Class[] cArg = new Class[1];
cArg[0] = byte[].class; // not Byte.class
Constructor<?> ct = myClass.getConstructor(cArg);

Object specialPacket = ct.newInstance(data);
Method mt = myClass.getMethod("setHeader", byte[].class)
mt.invoke(specialPacket, data);
packet = specialPacket;

答案 2 :(得分:0)

你可以使用工厂模式(例如http://alvinalexander.com/java/java-factory-pattern-example)吗?

类似于:

interface Packet {
     void parse(Datatype data);
     void setHeaders (Datatype data);
}

class PingPacket implements Packet {
    void parse (Datatype data) {
      ....
      ....
    }

    void setHeaders(Datatype data) {
     ...
     ...
    }
}

class ErrorPacket implements Packet {
    void parse (Datatype data) {
     .....
     .....
    }

    void setHeaders(Datatype data) {
     ...
     ...
    }
}

class PacketFactory {
  Packet getInstance(int packetId) {
     if (packetId == Ping_ID) {
         ...
         ...
         return new PingPacket();
     } else if (packetId == ERROR_ID) {
        ...
        ...
        return new ErrorPacket();
       }
  }
}