在 java 中实现正确的 C# 事件(不是委托)
Implementing a proper C# event (not delegate) in java
正如在 this SO 回答和同一问题中的其他帖子中已经提到的,可以使用接口或 Java FuncationInterfaces.
实现 C# 委托
但是我希望在 Java 中实现一个合适的事件模型而不是委托模型。两者的区别简述见this。尤其是第一条评论。
以下是我目前尝试过的方法:
Event.java
public class Event {
public interface EventHandler{
void invoke();
}
private Set<EventHandler> mEventHandlers = new HashSet<>();
public void add(EventHandler eventHandler){
mEventHandlers.add(eventHandler);
}
public void remove(EventHandler eventHandler){
mEventHandlers.remove(eventHandler);
}
public void invoke(){
for(EventHandler eventHandler : mEventHandlers){
if(eventHandler!=null) {
eventHandler.invoke();
}
}
}
}
EventPubisher.java
public class EventPublisher {
public Event ValueUpdatedEvent;
public void UpdateValue(){
ValueUpdatedEvent.invoke();
}
}
EventConsumer.java
public class EventConsumer {
EventPublisher ep = new EventPublisher();
public EventConsumer(){
ep.ValueUpdatedEvent.add(this::ValueUpdatedEventHandler);
}
private void ValueUpdatedEventHandler(){
// do stuff
}
}
这种设计的问题在于我也可以编写如下代码:
public class EventConsumer {
.....
private void abuse(){
ep.ValueUpdatedEvent.invoke();
}
}
而这正是事件所限制的。该事件应该只从声明 class 而不是从外部引发。
正如@Jon Skeet 在评论中提到的,更改如下代码满足我的要求:
EventPubisher.java
public class EventPublisher {
private final Event ValueUpdatedEvent = new Event();
public void addEventHandler(Event.EventHandler eventHandler){
ValueUpdatedEvent.add(eventHandler);
}
public void removeEventHandler(Event.EventHandler eventHandler){
ValueUpdatedEvent.remove(eventHandler);
}
public void UpdateValue(){
ValueUpdatedEvent.invoke();
}
}
EventConsumer.java
public class EventConsumer {
.....
private void abuse(){
// ep.ValueUpdatedEvent.invoke(); //Compilation error
}
}
如果您想避免对发布商使用 add/remove 方法,可以使用以下代码吗?它使用两个 类(Event 和 EventImpl)来分隔 add/remove 和调用,因此可以使调用对发布者私有。它是通用的,因此可以使用不同的侦听器接口。
此代码节省了大量重复的样板文件,但您认为它会被视为惯用的@JonSkeet 吗?
这是活动 类:
class Event<TListener> {
private final Set<TListener> listeners;
Event(Set<TListener> listeners) {
this.listeners = listeners;
}
public void add(TListener listener) {
listeners.add(listener);
}
public void remove(TListener listener) {
listeners.remove(listener);
}
}
class EventImpl<TListener> {
private Set<TListener> listeners = new HashSet<>();
private Event<TListener> event = new Event<>(listeners);
Event<TListener> getEvent() {
return event;
}
interface Invoker<TListener> {
void invoke(TListener listener);
}
public void invoke(Invoker<TListener> invoker) {
for (TListener listener : listeners){
if (listener != null) {
invoker.invoke(listener);
}
}
}
}
这里是一个发布者和订阅者的例子,有一些测试代码来练习它们:
class MyEventPublisher {
interface Listener {
void listenToThis(int intArg, String stringArg, Object... etc);
}
private EventImpl<Listener> eventImpl = new EventImpl<>();
Event<Listener> getEvent() {
return eventImpl.getEvent();
}
void somethingCausingAnEvent() {
eventImpl.invoke(
listener -> listener.listenToThis(1, "blah", 10,11, 12));
}
}
class MyEventSubscriber {
private String eventRecord = "";
MyEventSubscriber(MyEventPublisher publisher) {
publisher.getEvent().add(
(intArg, stringArg, etc) -> eventRecord += intArg + stringArg + Arrays.toString(etc));
}
String getEventRecord() {
return eventRecord;
}
}
public class TestEvents {
@Test
public void testEvent() {
MyEventPublisher p = new MyEventPublisher();
MyEventSubscriber s = new MyEventSubscriber(p);
p.somethingCausingAnEvent();
assertEquals("1blah[10, 11, 12]", s.getEventRecord());
}
}
正如在 this SO 回答和同一问题中的其他帖子中已经提到的,可以使用接口或 Java FuncationInterfaces.
但是我希望在 Java 中实现一个合适的事件模型而不是委托模型。两者的区别简述见this。尤其是第一条评论。
以下是我目前尝试过的方法:
Event.java
public class Event {
public interface EventHandler{
void invoke();
}
private Set<EventHandler> mEventHandlers = new HashSet<>();
public void add(EventHandler eventHandler){
mEventHandlers.add(eventHandler);
}
public void remove(EventHandler eventHandler){
mEventHandlers.remove(eventHandler);
}
public void invoke(){
for(EventHandler eventHandler : mEventHandlers){
if(eventHandler!=null) {
eventHandler.invoke();
}
}
}
}
EventPubisher.java
public class EventPublisher {
public Event ValueUpdatedEvent;
public void UpdateValue(){
ValueUpdatedEvent.invoke();
}
}
EventConsumer.java
public class EventConsumer {
EventPublisher ep = new EventPublisher();
public EventConsumer(){
ep.ValueUpdatedEvent.add(this::ValueUpdatedEventHandler);
}
private void ValueUpdatedEventHandler(){
// do stuff
}
}
这种设计的问题在于我也可以编写如下代码:
public class EventConsumer {
.....
private void abuse(){
ep.ValueUpdatedEvent.invoke();
}
}
而这正是事件所限制的。该事件应该只从声明 class 而不是从外部引发。
正如@Jon Skeet 在评论中提到的,更改如下代码满足我的要求:
EventPubisher.java
public class EventPublisher {
private final Event ValueUpdatedEvent = new Event();
public void addEventHandler(Event.EventHandler eventHandler){
ValueUpdatedEvent.add(eventHandler);
}
public void removeEventHandler(Event.EventHandler eventHandler){
ValueUpdatedEvent.remove(eventHandler);
}
public void UpdateValue(){
ValueUpdatedEvent.invoke();
}
}
EventConsumer.java
public class EventConsumer {
.....
private void abuse(){
// ep.ValueUpdatedEvent.invoke(); //Compilation error
}
}
如果您想避免对发布商使用 add/remove 方法,可以使用以下代码吗?它使用两个 类(Event 和 EventImpl)来分隔 add/remove 和调用,因此可以使调用对发布者私有。它是通用的,因此可以使用不同的侦听器接口。
此代码节省了大量重复的样板文件,但您认为它会被视为惯用的@JonSkeet 吗?
这是活动 类:
class Event<TListener> {
private final Set<TListener> listeners;
Event(Set<TListener> listeners) {
this.listeners = listeners;
}
public void add(TListener listener) {
listeners.add(listener);
}
public void remove(TListener listener) {
listeners.remove(listener);
}
}
class EventImpl<TListener> {
private Set<TListener> listeners = new HashSet<>();
private Event<TListener> event = new Event<>(listeners);
Event<TListener> getEvent() {
return event;
}
interface Invoker<TListener> {
void invoke(TListener listener);
}
public void invoke(Invoker<TListener> invoker) {
for (TListener listener : listeners){
if (listener != null) {
invoker.invoke(listener);
}
}
}
}
这里是一个发布者和订阅者的例子,有一些测试代码来练习它们:
class MyEventPublisher {
interface Listener {
void listenToThis(int intArg, String stringArg, Object... etc);
}
private EventImpl<Listener> eventImpl = new EventImpl<>();
Event<Listener> getEvent() {
return eventImpl.getEvent();
}
void somethingCausingAnEvent() {
eventImpl.invoke(
listener -> listener.listenToThis(1, "blah", 10,11, 12));
}
}
class MyEventSubscriber {
private String eventRecord = "";
MyEventSubscriber(MyEventPublisher publisher) {
publisher.getEvent().add(
(intArg, stringArg, etc) -> eventRecord += intArg + stringArg + Arrays.toString(etc));
}
String getEventRecord() {
return eventRecord;
}
}
public class TestEvents {
@Test
public void testEvent() {
MyEventPublisher p = new MyEventPublisher();
MyEventSubscriber s = new MyEventSubscriber(p);
p.somethingCausingAnEvent();
assertEquals("1blah[10, 11, 12]", s.getEventRecord());
}
}