查看: 685|回复: 0

[Java语言] 浅谈用java实现事件驱动机制

发表于 2017-10-2 07:50:50
尚学堂AD

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

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

  1. //定义事件和对应的委托
  2. public event MyDelegate Click;
  3. public delegate void MyDelegate();
  4. //定义委托
  5. void OnClick(){
  6. console.writeline("you just clicked me!");
  7. }
  8. //将委托与事件关联
  9. Click += OnClick;
  10. //触发事件
  11. Click();
复制代码

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

观察者模式

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

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

  1. /*事件类*/
  2. public Event {
  3. //与事件相关的事件处理函数
  4. public ArrayList<Callback> callbackList;
  5. //事件触发函数
  6. public void emit(){
  7. for(Callback cb : callbackList){
  8. cb.run();
  9. }
  10. }
  11. //注册事件处理函数
  12. public registerCallback(Callback cb){
  13. callbackList.add(cb);
  14. }
  15. }
  16. /*事件处理函数类*/
  17. public interface Callback {
  18. void run();
  19. }
  20. public OnClick implements Callback {
  21. //函数
  22. public void run(){
  23. System.out.println("you just clicked me!");
  24. }
  25. /*实现事件驱动*/
  26. Event e = new Event();
  27. //将OnClick事件处理函数注册到事件中
  28. e.registerCallback(new OnClick());
  29. //触发事件
  30. e.emit();
复制代码

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

利用反射

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

  1. /*事件处理类*/
  2. public class EventHandler {
  3. //事件源
  4. private Object sender;
  5. //事件处理函数名称(用于反射)
  6. private String callback;
  7. public EventHandler(Object sender, String callback){
  8. this.sender = sender;
  9. this.callback = callback;
  10. }
  11. //事件触发
  12. public void emit(){
  13. Class senderType = this.sender.getClass();
  14. try {
  15. //获取并调用事件源sender的事件处理函数
  16. Method method = senderType.getMethod(this.callback);
  17. method.invoke(this.sender);
  18. } catch (Exception e2) {
  19. e2.printStackTrace();
  20. }
  21. }
  22. }
  23. /*事件源*/
  24. public class Button(){
  25. /*可以在此设置Button类的相关属性,比如名字等*/
  26. private String name;
  27. ...
  28. //事件处理函数
  29. public void onClick(){
  30. System.out.println("you just clicked me!");
  31. }
  32. }
  33. /*实现事件驱动机制*/
  34. Button b = new Button();
  35. if(/*收到按钮点击信号*/){
  36. EventHandler e = new EventHandler(b, "onClick");
  37. e.emit();
  38. }
复制代码

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

  1. public class EventArgs {
  2. //参数
  3. String p1;
  4. Integer p2;
  5. ...
  6. }
  7. //onClick事件处理函数改写
  8. public void onClick(Object sender, EventArgs e){
  9. //参数e提供更多的信息
  10. System.out.println("Hello, you clicked me! " + e.p1 + e.p2);
  11. }
  12. //触发函数emit改写
  13. public void emit(EventArgs e){
  14. Class senderType = this.sender.getClass();
  15. try {
  16. //获取并调用事件源sender的事件处理函数
  17. Method method = senderType.getMethod(this.callback, this.getClass(), e.getClass());
  18. method.invoke(this.sender, this.sender, e);
  19. } catch (Exception e2) {
  20. e2.printStackTrace();
  21. }
  22. }
复制代码

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持程序员之家。



回复

使用道具 举报