如何在 java 中创建某种事件框架?
How to create some sort of event framework in java?
我没有 GUI(我的 classes 是 Minecraft Mod 的一部分)。我希望能够模仿 C# 事件框架:class 声明事件并让其他人订阅它们。
我的第一个方法是创建一个名为 EventArgs
的 class,然后执行如下操作:
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;
}
}
}
然后 class 会做这样的事情:
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);
}
}
}
这种天真的方法的最大问题是它通过将 raise
公开为 public 来破坏适当的封装。我看不出解决办法,也许我的整个模式都是错误的。我想要一些想法。
还有一个相关的问题:我希望事件在引发事件的方法returns之后立即引发。有没有办法使用线程或其他构造来同步它?调用者代码当然不能参与同步任务。它必须对它完全透明。
这里最好的做法是首先避免实现自己的事件框架,而是依赖一些现有的库。开箱即用 Java 提供 EventListener
,至少您可以遵循那里记录的模式。即使对于非 GUI 应用程序,大多数建议也适用。
超越 JDK Guava 提供了几个可能的选项,具体取决于您的具体用例。
最有可能的候选人是EventBus
,即:
allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other).
或ListenableFuture
(和ListeningExecutorService
)其中:
allows you to register callbacks to be executed once [a task submitted to an Executor
] is complete, or if the computation is already complete, immediately. This simple addition makes it possible to efficiently support many operations that the basic Future interface cannot support.
或者 Service
API 其中:
represents an object with an operational state, with methods to start and stop. For example, webservers, RPC servers, and timers can implement the Service interface. Managing the state of services like these, which require proper startup and shutdown management, can be nontrivial, especially if multiple threads or scheduling is involved.
这 API 同样可以让您 register listeners 响应服务中的状态变化。
即使 none 这些选项直接适用于您的用例,请查看 Guava's source code 以获取您可以尝试模拟的事件驱动行为和侦听器的示例。
我没有 GUI(我的 classes 是 Minecraft Mod 的一部分)。我希望能够模仿 C# 事件框架:class 声明事件并让其他人订阅它们。
我的第一个方法是创建一个名为 EventArgs
的 class,然后执行如下操作:
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;
}
}
}
然后 class 会做这样的事情:
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);
}
}
}
这种天真的方法的最大问题是它通过将 raise
公开为 public 来破坏适当的封装。我看不出解决办法,也许我的整个模式都是错误的。我想要一些想法。
还有一个相关的问题:我希望事件在引发事件的方法returns之后立即引发。有没有办法使用线程或其他构造来同步它?调用者代码当然不能参与同步任务。它必须对它完全透明。
这里最好的做法是首先避免实现自己的事件框架,而是依赖一些现有的库。开箱即用 Java 提供 EventListener
,至少您可以遵循那里记录的模式。即使对于非 GUI 应用程序,大多数建议也适用。
超越 JDK Guava 提供了几个可能的选项,具体取决于您的具体用例。
最有可能的候选人是EventBus
,即:
allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other).
或ListenableFuture
(和ListeningExecutorService
)其中:
allows you to register callbacks to be executed once [a task submitted to an
Executor
] is complete, or if the computation is already complete, immediately. This simple addition makes it possible to efficiently support many operations that the basic Future interface cannot support.
或者 Service
API 其中:
represents an object with an operational state, with methods to start and stop. For example, webservers, RPC servers, and timers can implement the Service interface. Managing the state of services like these, which require proper startup and shutdown management, can be nontrivial, especially if multiple threads or scheduling is involved.
这 API 同样可以让您 register listeners 响应服务中的状态变化。
即使 none 这些选项直接适用于您的用例,请查看 Guava's source code 以获取您可以尝试模拟的事件驱动行为和侦听器的示例。