我正在制作一款多人游戏,它大量使用可序列化的Event
类通过网络发送消息。我希望能够基于一个常数重构Event
的适当子类。
到目前为止,我选择了以下解决方案:
public class EventFactory {
public static Event getEvent(int eventId, ByteBuffer buf) {
switch (eventId){
case Event.ID_A:
return EventA.deserialise(buf);
case Event.ID_B:
return EventB.deserialise(buf);
case Event.ID_C:
return EventC.deserialise(buf);
default:
// Unknown Event ID
return null;
}
}
}
然而,这给我的印象是非常冗长,并且每次我创建一个新的Event
类型时都要添加一个新的'case'语句。
我知道有两种方法可以做到这一点,但似乎没有一种更好*:
- 创建常量->事件子类的映射,并使用clazz.newInstance()实例化它们(使用空构造函数),然后使用clazz.initialiase(buf)提供必要的参数。
- 创建一个常量->事件子类的映射,并使用反射在适当的类中找到并调用正确的方法。
是否有比我现在使用的更好的方法?也许我忽视上面提到的其他选择是不明智的?
您可以使用HashMap<Integer,Event>
来获取eventID的正确事件。添加或删除事件将很容易,并且随着代码的增长,与切换案例解决方案和速度相比,这更容易维护,而且这应该比切换案例解决方案更快。
static
{
HashMap<Integer,Event> eventHandlerMap = new HashMap<>();
eventHandlerMap.put(eventId_A, new EventHandlerA());
eventHandlerMap.put(eventId_B, new EventHandlerB());
............
}
代替你的switch语句,现在你可以使用:
Event event = eventHandlerMap.get(eventId);
if(event!=null){
event.deserialise(buf);
}
如果你不怕反射,你可以使用:
private static final Map<Integer, Method> EVENTID_METHOD_MAP = new LinkedHashMap<>();
static {
try {
for (Field field : Event.class.getFields())
if (field.getName().startsWith("ID_")) {
String classSuffix = field.getName().substring(3);
Class<?> cls = Class.forName("Event" + classSuffix);
Method method = cls.getMethod("deserialize", ByteBuffer.class);
EVENTID_METHOD_MAP.put(field.getInt(null), method);
}
} catch (IllegalAccessException|ClassNotFoundException|NoSuchMethodException e) {
throw new ExceptionInInitializerError(e);
}
}
public static Event getEvent(int eventId, ByteBuffer buf)
throws InvocationTargetException, IllegalAccessException {
return (Event) EVENTID_METHOD_MAP.get(eventId).invoke(null, buf);
}
这个解决方案要求int ID_N
总是映射到class EventN
,其中N
可以是任何字符串,其中所有字符返回java.lang.Character.isJavaIdentifierPart(c)
方法的true
。此外,class EventN
必须定义一个名为deserialize
的静态方法,该方法带有一个返回Event
的ByteBuffer
参数。
你也可以检查field
是否是静态的,然后再尝试获取它的字段值。我现在忘记怎么做了