浅谈用java实现事件驱动机制

作者:关注歌声楚楚 时间:2022-07-12 18:06:03 

由于项目需求,需要为Java提供一套支持事件驱动机制的类库,可以实现类似于C#中的event和delegate机制。众所周知,Java语言本身以及其标准库中并没有提供事件驱动机制的相关接口,虽然Swing(我且认为其不属于标准库,因为一般没人用:)中存在相关的类支持该机制以实现组件的事件处理,但它毕竟是与GUI相耦合的,而在其它类型的应用程序中使用起来显得就有些别扭,缺乏通用性。因此有必要实现一套通用的Java事件驱动机制类库,然后将其应用于通用的Java应用程序当中,虽然这并不是什么难事:)

让我们先考察一下C#的事件驱动机制编写方法。C#中提供的event关键字可以很容易的用来定义一个事件,然后通过向事件中添加事件处理函数(在C#中一般用委托(delegate)来引用一个函数),触发事件就可以调用相关的处理函数,也即是事件驱动的过程。例如:


//定义事件和对应的委托
public event MyDelegate Click;
public delegate void MyDelegate();

//定义委托
void OnClick(){
 console.writeline("you just clicked me!");
}

//将委托与事件关联
Click += OnClick;

//触发事件
Click();

上面的代码就是用C#实现的事件驱动机制的一个简单的例子,可见是非常简单的,这都源于C#在语言层面(其实是CLR)提供的便利。遗憾的是Java并不提供这样的便利,需要人为去实现。下面本文将提供两种实现事件驱动机制的方法,仅供参考。

观察者模式

观察者模式是一种常用的设计模式,观察者(Observer)先通过订阅被观察对象(Subject),这样一旦被观察者(Subject)发生某种变化,就会将变化通知观察者(Observer)。

这种设计模式刚好可以用于事件驱动机制,事件(event)相当于被观察对象(Subject),一旦事件被触发,就会调用事件处理函数,可见事件处理函数(C#中的委托)可以看作是观察者。因此可以像如下这样实现上文中的功能。


/*事件类*/
public Event {
 //与事件相关的事件处理函数
 public ArrayList<Callback> callbackList;

//事件触发函数
 public void emit(){
   for(Callback cb : callbackList){
     cb.run();
   }
 }

//注册事件处理函数
 public registerCallback(Callback cb){
   callbackList.add(cb);
 }
}

/*事件处理函数类*/
public interface Callback {
 void run();
}

public OnClick implements Callback {
 //函数
 public void run(){
   System.out.println("you just clicked me!");
 }

/*实现事件驱动*/
Event e = new Event();
//将OnClick事件处理函数注册到事件中
e.registerCallback(new OnClick());
//触发事件
e.emit();

上面的Java代码实现了一种简单的事件驱动机制,原理很简单,是一种典型的观察者模式的应用案例。

利用反射

Java语言提供强大的反射功能,可以在运行时获取类的各个组成部分(比如类名、类成员函数、类属性等等)并对其进行操作。下面使用反射来实现简单的事件驱动机制。


/*事件处理类*/
public class EventHandler {
 //事件源
 private Object sender;
 //事件处理函数名称(用于反射)
 private String callback;

public EventHandler(Object sender, String callback){
   this.sender = sender;
   this.callback = callback;
 }

//事件触发
 public void emit(){
 Class senderType = this.sender.getClass();
 try {
   //获取并调用事件源sender的事件处理函数
   Method method = senderType.getMethod(this.callback);
   method.invoke(this.sender);
   } catch (Exception e2) {
     e2.printStackTrace();
   }
 }
}

/*事件源*/
public class Button(){
 /*可以在此设置Button类的相关属性,比如名字等*/
 private String name;
 ...

//事件处理函数
 public void onClick(){
   System.out.println("you just clicked me!");
 }
}

/*实现事件驱动机制*/
Button b = new Button();
if(/*收到按钮点击信号*/){
 EventHandler e = new EventHandler(b, "onClick");
 e.emit();
}

上述代码展示了利用反射实现的事件驱动机制,利用反射机制的好处是其具有强大的扩展性,比如我的事件处理函数中可以引入一个EventArgs的形参,从而可以让事件本身带有参数,这样就可以让事件携带更多的信息,改写后的事件处理函数如下方的代码所示:


public class EventArgs {
 //参数
 String p1;
 Integer p2;
 ...

}

//onClick事件处理函数改写
public void onClick(Object sender, EventArgs e){
 //参数e提供更多的信息
 System.out.println("Hello, you clicked me! " + e.p1 + e.p2);
}

//触发函数emit改写
public void emit(EventArgs e){
Class senderType = this.sender.getClass();
try {
 //获取并调用事件源sender的事件处理函数
 Method method = senderType.getMethod(this.callback, this.getClass(), e.getClass());
 method.invoke(this.sender, this.sender, e);
 } catch (Exception e2) {
   e2.printStackTrace();
 }
}

是不是似曾相识?没错,和用C#写Winform窗体时,Visual studio为你自动生成的事件处理函数(代码中的onClick函数)几乎具有完全相同的形式,但此时我们是用Java实现的。

当然,除了以上提到的两种方法可以实现Java的事件驱动机制以外,还有一些其它的方法,比如可以利用Java的内部类来实现,笔者也曾写过一些示例代码,这里就不再冗言了,留待以后再讲。

来源:http://www.jianshu.com/p/ccd468c6be8a

标签:java,事件处理机制
0
投稿

猜你喜欢

  • 使用SpringBoot注解方式处理事务回滚实现

    2023-04-30 13:59:19
  • 25个最好的免费Eclipse插件

    2021-09-21 10:56:24
  • SpringBoot @Validated注解实现参数分组校验的方法实例

    2023-01-02 11:18:50
  • C#执行外部命令的方法

    2022-12-21 18:03:32
  • Android获取手机文件夹及文件列表的方法

    2022-04-22 22:22:32
  • 使用itextpdf解决PDF合并的问题

    2023-09-21 04:47:16
  • Android实现背景可滑动登录界面 (不压缩背景弹出键盘)

    2023-11-14 11:09:07
  • java的main方法中调用spring的service方式

    2023-03-29 00:12:16
  • 浅谈byte和长度为8的boolean数组互相转换

    2023-11-07 00:34:37
  • Android Studio缓存文件夹配置教程

    2022-11-28 12:04:51
  • java如何使用Lombok更优雅地编码

    2022-07-24 23:24:50
  • c#使用file.copy实现文件备份示例

    2021-06-03 05:13:13
  • C#实现多线程下载文件的方法

    2022-04-24 10:59:43
  • C# 打开蓝牙设置界面的两种方法

    2021-12-22 04:51:43
  • 通过实例解析传统jar包引用方式

    2022-08-01 05:14:06
  • Android SurfaceView画板操作

    2022-07-10 15:12:23
  • Android利用OpenGLES绘制天空盒实例教程

    2023-11-19 16:07:27
  • 在C#中global关键字的作用及其用法

    2021-12-24 04:33:19
  • Java八种基本变量作为类的成员变量的默认值操作

    2022-06-25 04:55:58
  • Java线程公平锁和非公平锁的差异讲解

    2022-06-26 09:59:09
  • asp之家 软件编程 m.aspxhome.com