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。我看不到解决方法,也许我的整个模式是错误的。我想要一些想法。
还有一个相关的问题:我希望在引发事件的方法返回后立即引发事件。有没有一种方法可以使用线程或其他一些结构来同步它?当然,调用者代码不能参与同步任务。它必须对它完全透明。
最好的做法是首先避免实现自己的事件框架,而要依赖一些现有的库。Java提供了开箱即用的功能EventListener,至少您可以遵循此处记录的模式。即使对于非GUI应用程序,大多数建议也适用。
超越JDK Guava提供了几种可能的选择,具体取决于您的实际用例。
最可能的候选人是EventBus,其中:
允许组件之间进行发布-订阅式通信,而无需组件之间显式注册(因此彼此了解)。
或ListenableFuture(和ListeningExecutorService)其中:
允许您注册一旦[提交给
Executor] 的任务完成,或者如果计算已经完成,立即执行的回调。这个简单的添加使其可以有效地支持基本Future接口无法支持的许多操作。
或ServiceAPI:
代表具有操作状态的对象,并带有启动和停止方法。例如,Web服务器,RPC服务器和计时器可以实现Service接口。管理此类需要适当启动和关闭管理的服务状态可能很简单,尤其是在涉及多个线程或调度的情况下。
该API同样允许您注册侦听器以响应服务中的状态更改。
即使这些选项都不直接适合您的用例,请查看Guava的源代码以获取事件驱动的行为和您可以尝试模仿的侦听器的示例。