Java观察者/对象侦听器(游戏引擎)

Java观察者/对象侦听器(游戏引擎),java,Java,我正在开发一个游戏引擎,关于这一点,我的最后一个问题是,我可以用什么好的方法来制作“观察者”或听众。一位用户建议我应该使用Java的EventObject类来继承并生成侦听器接口。然而,这并没有给我提供很好的灵活性 以下是处理程序注释,说明方法是侦听器中的事件处理程序: @Retention(RetentionPolicy.CLASS) @Target(ElementType.METHOD) public @interface Handler {} 下面是Event的基类,它与EventObj

我正在开发一个游戏引擎,关于这一点,我的最后一个问题是,我可以用什么好的方法来制作“观察者”或听众。一位用户建议我应该使用Java的EventObject类来继承并生成侦听器接口。然而,这并没有给我提供很好的灵活性

以下是处理程序注释,说明方法是侦听器中的事件处理程序:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface Handler {}
下面是Event的基类,它与EventObject基本相同(但我迟早会添加抽象方法):

下面是Listener类,它是空的:

public interface Listener {}
下面是ListenerHandler类,用于处理所有侦听器。你可以在这里注册和注销它们。为了更好地使用,我将在稍后编辑注册/取消注册方法:

public class ListenerHandler {

    private ArrayList<Listener> listeners;

    public ListenerHandler() {
        this.listeners = new ArrayList<Listener>();
    }

    public void registerListener(Listener l) {
        listeners.add(l);
    }

    public void unregisterListener(Listener l) {
        listeners.remove(l);
    }

    public void onEvent(Event event) {
        for(Listener l : listeners) {
            Class<?> c = l.getClass();

            Method[] methods = c.getDeclaredMethods();

            for(Method m : methods) {
                if(m.isAccessible()) {
                    if(m.isAnnotationPresent(Handler.class)) {
                        Class<?>[] params = m.getParameterTypes();

                        if(params.length > 1) {
                            continue;
                        }

                        Class<?> par = params[0];

                        if(par.getSuperclass().equals(Event.class)) {
                            try {
                                m.invoke(this, event);
                            }catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
    }
}
公共类ListenerHandler{
私有ArrayList侦听器;
公共ListenerHandler(){
this.listeners=new ArrayList();
}
公共无效寄存器侦听器(侦听器l){
添加(l);
}
公共无效取消注册侦听器(侦听器l){
删除(l);
}
公共无效事件(事件){
for(侦听器l:侦听器){
c类=l.getClass();
方法[]方法=c.getDeclaredMethods();
用于(方法m:方法){
如果(m.isAccessible()){
if(m.isAnnotationPresent(Handler.class)){
类[]params=m.getParameterTypes();
如果(参数长度>1){
继续;
}
类PAR=PARAMS〔0〕;
if(par.getSuperclass().equals(Event.class)){
试一试{
m、 调用(此事件);
}捕获(IllegalAccessException | IllegalArgumentException | InvocationTargetException e){
e、 printStackTrace();
}
}
}
}
}
}
}
}
据我所知,获取一个类的所有方法需要使用大量内存。我不打算假设是这样,但我相信有更好的方法,因为这将是一个包含许多组件的游戏引擎


我想知道实现这一点的最佳方法,或者我是否做得对。我还想知道是否有人能在不占用游戏内存的情况下,以任何方式帮助我改进这一点(到目前为止,这还不是什么大问题——“游戏引擎”甚至还没有接近渲染任何东西)

我试图将其保留为一个非常简单的示例,并将以不同的观点对其进行评论: 第一次参加成就班:

import java.util.Observable;
    public class Achievement extends Observable {
    public static class AchievementDetails {}
public Achievement() {
        addObserver(EventsListener.getInstance());
    }
    public void achievementReached() {
        AchievementDetails achievemetDetails = null;
        setChanged();
        notifyObservers(achievemetDetails);
    }
}
然后是事件侦听器类:

import com.test.Achievement.AchievementDetails;
public class EventsListener implements Observer {
    private static EventsListener instance = new EventsListener();
    public static EventsListener getInstance() {
        return instance;
    }
    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof Achievement) {
            AchievementDetails achievemetDetails = (AchievementDetails) arg;
            //do some logic here
        }
    }
}

唯一缺少的是创建一个成就实例(将事件侦听器注册到自身)并处理它的生命周期。

有什么理由不使用
接口来描述事件侦听器,过早的优化是万恶之源,而不是跳过注释环。先让它工作,然后再优化。我试图理解为什么在这里使用注释。如果您使用,您会发现实现您描述的内容非常容易,而无需在运行时进行注释和所有这些动态分析。你能详细说明一下你到底想实现什么吗?我想让它成为一种触发事件并将其发送到全局方法(在EventHandler类中)然后在那里进行处理的方式。例如,玩家死亡并将PlayerDeathEvent的实例发送到OneEvent。将调用使用带有处理程序注释的方法注册并“侦听”该事件的任何侦听器(例如,作为参数的PlayerDeathEvent)。不知何故,我发现它比为每一个事件或对象制作XListener要好得多,我觉得这完全没有必要。另一个例子是成就。为了增强上面的代码,我建议创建一个抽象类,它是所有事件的父类,并且是可观察类的子类。这个父类将包含一个返回枚举的抽象方法。枚举包括所有可能的事件类型。这是为了增强EventsListener中更新方法的代码,以使用switch而不是InstanceOffi建议根据所需操作使用不同的事件侦听器。并将这些侦听器注册到相应的可观察对象。例如,您可能希望在出现死机、超时或服务器宕机的情况下结束游戏。在本例中,您将创建一个EndGameAction(观察者)并将此操作注册到所有三个观察者(DeAtheEvent、TimeOutEvent和ServerIsDownEvent)中,我现在这样做是不是一个坏主意?我确信它并没有看起来那么糟糕,但我想知道。如果将一个可观察对象注册为可观察对象将很难维护,并且您需要另一个解决方案,我认为您需要看看Java注释处理工具(Java APT)这里:在这种情况下,您可以创建自己的编译时注释,并在编译时处理该注释(使用APT),并生成一个java类,将每个可观察对象注册到它的Obersver(需要使用注释放置链接)@Sparta我不认为在这种情况下有什么好主意和坏主意。您的代码运行良好。我只是为您提供另一种方法,对其进行不同的增强。在您的案例中,唯一需要注意的是,您也不希望在运行时进行大量处理。因此,我建议您在编译时而不是运行时进行注释处理(如果适用)。
import com.test.Achievement.AchievementDetails;
public class EventsListener implements Observer {
    private static EventsListener instance = new EventsListener();
    public static EventsListener getInstance() {
        return instance;
    }
    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof Achievement) {
            AchievementDetails achievemetDetails = (AchievementDetails) arg;
            //do some logic here
        }
    }
}