基于Java

时间:2015-07-07 20:33:48

标签: java reflection instantiation

我正在制作一个多人游戏,它大量使用可序列化的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”语句。

我知道其他两种方法可以实现这一点,但似乎都没有好转*:

  1. 创建常量映射 - >事件子类,并使用clazz.newInstance()实例化它们(使用空构造函数),然后使用clazz.initialiase(buf)提供必要的参数。
  2. 创建常量映射 - >事件子类,并使用反射在适当的类中查找和调​​用正确的方法。
  3. 有没有比我使用的方法更好的方法?我可能不理会上面提到的替代方案吗?

    *注意:在这种情况下,更好的意味着更简单/更清洁,但不会影响速度太快。

2 个答案:

答案 0 :(得分:0)

您可以使用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);
   }

答案 1 :(得分:0)

如果你不害怕反思,你可以使用:

    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可以是任何字符串,其中所有字符都返回true方法java.lang.Character.isJavaIdentifierPart(c)。此外,class EventN必须定义一个名为deserialize的静态方法,其中一个ByteBuffer参数返回Event

在尝试获取其字段值之前,您还可以检查field是否为静态。我现在忘记了如何做到这一点。