如何在Java中创建某种事件框架?

Fin*_*man 2 java events method-reference

我没有GUI(我的课程是Minecraft Mod的一部分)。我希望能够模仿C#事件框架:一个类声明事件并让其他人订阅它们。

我的第一种方法是创建一个名为的类EventArgs,然后执行以下操作:

public class EventArgs
{
    public boolean handled;
}

@FunctionalInterface
public interface IEventHandler<TEvtArgs extends EventArgs>
{
    public void handle(Object source, TEvtArgs args);
}

public class Event<TEvtArgs extends EventArgs>
{
    private final Object owner;
    private final LinkedList<IEventHandler<TEvtArgs>> handlers = new LinkedList<>();

    public Event(Object owner)
    {
        this.owner = owner;
    }

    public void subscribe(IEventHandler<TEvtArgs> handler)
    {
        handlers.add(handler);
    }

    public void unsubscribe(IEventHandler<TEvtArgs> handler)
    {
        while(handlers.remove(handler));
    }

    public void raise(TEvtArgs args)
    {
        for(IEventHandler<TEvtArgs> handler : handlers)
        {
            handler.handle(owner, args);
            if(args.handled)
                break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,一个类将执行以下操作:

public class PropertyChangedEvtArgs extends EventArgs
{
    public final Object oldValue;
    public final Object newValue;

    public PropertyChangedEvtArgs(final Object oldValue, final Object newValue)
    {
        this.oldValue = oldValue;
        this.newValue = newValue;
    }
}

public class SomeEventPublisher
{
    private int property = 0;
    private final Random rnd = new Random();
    public final Event<PropertyChangedEvtArgs> PropertyChanged = new Event<>(this);

    public void raiseEventOrNot(int value)
    {
        if(rnd.nextBoolean())//just to represent the fact that the event is not always raised
        {
            int old = property;
            property = value;
            PropertyChanged.raise(new PropertyChangedEvtArgs("old(" + old + ")", "new(" + value + ")"));
        }                
    }
}

public class SomeSubscriber
{
    private final SomeEventPublisher eventPublisher = new SomeEventPublisher();

    public SomeSubscriber()
    {
        eventPublisher.PropertyChanged.subscribe(this::handlePropertyAChanges);
    }

    private void handlePropertyAChanges(Object source, PropertyChangedEvtArgs args)
    {
        System.out.println("old:" + args.oldValue);
        System.out.println("new:" + args.newValue + "\n");
    }

    public void someMethod(int i)
    {
        eventPublisher.raiseEventOrNot(i);
    }
}

public class Main
{
    private static final SomeSubscriber subscriber = new SomeSubscriber();

    public static void main(String[] args)
    {
        for(int i = 0; i < 10; ++i)
        {
            subscriber.someMethod(i);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这种幼稚的方法最大的问题是,它通过公开展示来破坏适当的封装raise。我看不到解决方法,也许我的整个模式是错误的。我想要一些想法。

还有一个相关的问题:我希望在引发事件的方法返回立即引发事件。有没有一种方法可以使用线程或其他一些结构来同步它?当然,调用者代码不能参与同步任务。它必须对它完全透明。

dim*_*414 6

最好的做法是首先避免实现自己的事件框架,而要依赖一些现有的库。Java提供了开箱即用的功能EventListener,至少您可以遵循此处记录的模式。即使对于非GUI应用程序,大多数建议也适用。

超越JDK Guava提供了几种可能的选择,具体取决于您的实际用例。

最可能的候选人是EventBus,其中:

允许组件之间进行发布-订阅式通信,而无需组件之间显式注册(因此彼此了解)。

ListenableFuture(和ListeningExecutorService)其中:

允许您注册一旦[提交给Executor] 的任务完成,或者如果计算已经完成,立即执行的回调。这个简单的添加使其可以有效地支持基本Future接口无法支持的许多操作。

ServiceAPI:

代表具有操作状态的对象,并带有启动和停止方法。例如,Web服务器,RPC服务器和计时器可以实现Service接口。管理此类需要适当启动和关闭管理的服务状态可能很简单,尤其是在涉及多个线程或调度的情况下。

该API同样允许您注册侦听器以响应服务中的状态更改。

即使这些选项都不直接适合您的用例,请查看Guava的源代码以获取事件驱动的行为和您可以尝试模仿的侦听器的示例。