0

我有一个接收包(字节数组)的套接字类。每个包都有一个整数来标识它的类型。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

4

3 回答 3

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;

但是,在我看来,使用反射通常是最后的手段。考虑研究可能有助于解决此类问题 的其他模式,例如访问者模式。

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

于 2013-05-01T13:22:37.353 回答
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();
       }
  }
}
于 2013-05-01T13:49:04.240 回答
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;
于 2013-05-01T13:27:24.990 回答