java 中的event_用Java实现C#中的event

在C#中有一个event关键字,可以很方便的定义一个事件。

event EventHandler Something;

Something += DoSomething;

通过上面的两行,在C#中可以快速的将事件Something转发到DoSomething方法中。用习惯了这种事件的方法,反而不习惯java中的事件模型了。在Windows

Phone

7正式登录大陆之前,要暂时开发Android的游戏。为了不让以后的开发过程感觉不自在,暂时定义一个java的事件模型。

需求很清楚,要模拟C#中event的使用。首先需要定义一个事件句柄(EventHandler):

packagecom.wind.util;

importjava.util.ArrayList;

public classEventHandler {

privateArrayList listeners= newArrayList();

public synchronized voidadd(EventListener listener) {

// 判断在监听器列表中是否存在当前监听器对象if(listeners.indexOf(listener) < 0) {

listeners.add(listener);

}

}

public synchronized voidremove(EventListener listener){

listeners.remove(listener);

}

public voidinvoke(Object sender) {

// 构造事件对象EventArg e = newEventArg();

invoke(sender, e);

}

public voidinvoke(Object sender, Object... args) {

// 构造事件对象EventArg e = newEventArg(args);

invoke(sender, e);

}

public voidinvoke(Object sender, EventArg e) {

synchronized(listeners) {

// 构造事件对象for(inti = 0; i < listeners.size(); i++) {

listeners.get(i).doEvent(sender, e);

}

}

}

}

这里简单的解释一下,在java中有一个Observable类,实现了观察者模式。但是在其中有很多我用不上的方法,而且添加观察者为addObserver,看起来很不爽。索性就写了上面的这个EventHandler类。使用ArrayList来存储所有的监听器对象。提供了add和remove方法来添加和删除监听器(这里本来是采用addListener和removeListener的,但是在实际使用的时候代码过长,就去掉了)。提供了3个invoke重载来完成事件的调用,这个重载组主要是针对事件参数来进行的重载。

接下来就是定义事件参数:

packagecom.wind.util;

public classEventArg {

privateObject[] args= null;

publicEventArg() {}

publicEventArg(Object... args) {

this.args= args;

}

public intgetArgLength() {

if(args== null) {

return0;

} else{

returnargs.length;

}

}

publicObject getArg(intindex) {

if(args== null) return null;

if(args.length<= index) {

return null;

} else{

returnargs[index];

}

}

public booleanhasArg() {

returnargs.length> 0;

}

@OverridepublicString toString() {

if(args== null) return"";

returnargs.toString();

}

}

其实这个设计并不是太好,我在性能、扩展性之间进行了取舍,因为我暂时涉及不到太大的项目,而且事件的调用不是很普及(相对于每秒30次的刷新而言),将事件参数封装在Object数组args中。通过Java5的动态参数特性进行了封装。对args进行了第二层封装,并不直接对外提供。采用getArgLength和getArg两个方法配合,完成参数的获取操作,避免了对args进行非法修改。因为args本质上是Object对象,所以任何对象都可以方进去,扩展性非常高。稍微麻烦点的地方是获取参数的时候,容易引发转型错误,不过借鉴了Ruby等动态语言的设计思路后,转型错误并不是那么容易出现,毕竟事件的接收双方都是我自己定义的(这里的一个取舍在于我不是做大型商业框架,不需要考虑过多的健壮性)。

packagecom.wind.util;

public interfaceEventListener {

voiddoEvent(Object sender, EventArg e);

}

监听器EventListener是一个接口,任何接受的监听器都需要实现doEvent方法,两个参数Object

sender和EventArg

e。其实这里的sender是采用的.Net中的事件命名方式,主要是习惯了,就这样沿用了(毕竟半年后估计又转回.Net了)。光是以上的三个类、接口可能还看不出来和C#中的event有什么关联,不着急,我们来看看在实际的代码中如何使用。

首先在事件源的类中,需要定义句柄,这里我按照开篇的时候C#的代码来命名:

privateEventHandler somethingHandler= newEventHandler();

publicEventHandler something() { returnsomethingHandler; }

首先定义一个private的句柄,再通过一个something方法来返回句柄对象(这种设计模式参考Ruby中的虚方法模式,采用一个方法来模拟模拟的属性或者更改属性的名称、类型等定义)。虽然比起C#的代码要稍微繁琐一点,不过我认为这点繁琐还是可以接受的。

somethingHandler.invoke(this);

somethingHandler.invoke(this, arg1, arg2);

触发事件的过程非常简单,通过EventHandler中invoke的重载链,可以很方便的添加参数。由于采用了动态参数的特性,在调用的时候可以不用去管参数的类型和个数,用逗号隔开即可。怎样,有点感觉了吗?别着急,重点还在后面。

privateEventListener somethingListener= newEventListener() {

@Overridepublic voiddoEvent(Object sender, EventArg e) {

if(e.getArgLength() >= 2) {

System.out.println("something: arg1="+ e.getArg(0) + " arg2="+ e.getArg(1));

}

}

};

在需要监听事件的方法中,通过匿名内部类的方法实现了EventListener接口。打印了arg1和arg2。

component1.something().add(somethingListener);

添加监听的方法很简洁,是不是看到了类似C#中的语法了?可惜java不支持符号重载,要不然我也写成+=和-=的形式,呵呵。

总的来说这样封装的事件模型,效率是比较高的,由于没有采用Observable和EventObject,这三段代码和java.util的耦合是很低的,只引用了一个ArrayList类。这样在移植到Android的过程当中也不会出现什么问题。

关于效率问题,我觉得不是问题,在测试当中,一千万次委托调用,在1个监听器的情况下需要800ms,2个监听器需要1200ms。效率上是得到保障了的,在我的PC上每毫秒能执行一万次委托,这个速度可以忽略了,我相信就算在Android中,执行效率也不会太差。

2010年9月10日更新:

上次已经实现了类似C#中的事件。本着精益求精的精神,对事件模型进行了重构,首先将EventArg更名为EventArgs,名称上更加贴切。

然后对EventHandler和EventListener进行了泛型化,将EventArgs取代为,在需要使用EventArgs的情况下就声明EventHandler即可,如果需要其他类型,如Date类型的参数,只需要声明为EventHandler即可,在监听时采用EventListener,自然就接受了Date类型的参数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值