有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java用HashMap替换switch语句

我有一个socket类,它接收包(字节数组)。每个包都有一个整数以标识其类型。switch语句中的每种情况如下所示:

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;

因此,基本的想法是创建一个hashmap,其中包含数据包id和相应的数据包类,允许我创建专门的数据包。最后一段代码是用来替换我的switch语句的

问题:

我的解决方法正确吗?如果没有,请告诉我。 我如何实现我的代码的最后一部分,这到目前为止还没有意义(如果这是正确的方法)

EDIT:packet是抽象类Packet的一个对象,它被像PingRequest这样的每个特殊包扩展


共 (3) 个答案

  1. # 1 楼答案

    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. # 2 楼答案

    你能不能用工厂模式(例如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();
           }
      }
    }
    
  3. # 3 楼答案

    看来你的主意行得通。我认为代码应该是这样的:

    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。 编辑:添加了第二个使用枚举字段的替代解决方案