AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > JAVA编程

探讨与比较Java和.NET的事件处理框架

51自学网 2015-09-03 http://www.wanshiok.com

 

  事件的预订和发布

  Publisher必需能够接受多个subscribers的预订,所以在publisher当中必需维护预订者的列表以供将来发布事件使用。在Java语言的模式中,并没有提供特别的东西来帮助这件事,可以自己用collection 类来做,例如可以使用ArrayList 对象来做记录。Java的预订方法名的风格为addXXXListener(),因为在publisher端必需把subscriber 对象“添加”到预订者列表后面,如下图:


  对于subscriber来说,预订动作的内部处理是黑箱的,subscriber不用关心publisher是如何做预订记录的。参考以下代码片段:

class Publisher {
 private ArrayList listenerList = new ArrayList();
 public void addKeyListener(KeyListener l) {
  listenerList.add(l);
 }
 public void fireKeyPressedEvent(int keyCode) {
  Iterator iter = listenerList.iterator();
  while (iter.hasNext()) {
   KeyListener l = (KeyListener)iter.next();
   l.keyPressed(keyCode);
  }
 }
}

  当然这段代码只是简单的示意,如果要考虑多线程的安全问题,可能要在addKeyListener()前面加上synchronized;还有,有预订就必然相应的有“退订”(removeXXXListener()),在这里就不再把它写出来。

  如果每个publisher都要这样重复撰写这样的代码的确很麻烦。所以在.net中势必希望能够提供一种用来帮助publisher记录预订者,和发布事件的工具。按一般设计者的初步想法,一定是先提供一个辅助类来协助:


  从语法上考虑简化,add/remove动作应该可以用C++的operator=()、operator+=()和operator-=()来完成。像这样:

Publisher publisher = new Publisher();
...
publisher.KeyEventHandlerDelegate += KeyPressedHandler;
//等同于
//publisher.EventHandlerDelegate.add(KeyPressedHandler);

  如果可以这样撰写的话,确实很简单。不过在强制性类型(strongly-typed)语言系统中,必需精确的定义add()方法参数中的delegate 类型,这样似乎无法写出一个可以公用的基础类。所以在.NET中,是结合delegate关键字,通过简单的语法,借助编译器来帮我们自动生成相关的代码。于是把delegate的能力再予以加强了:

Event type definition:
public delegate void KeyPressedDelegate(int keyCode);

Publisher:
class Publisher
{
 public KeyPressedDelegate KeyPressed;
 ...
 void FireKeyPressedEvent(int KeyCode)
 {
  if (KeyPressed != null)
   //依次调用记录在KeyPressed中的所有方法
   KeyPressed(keyCode);
 }
}

Subscriber:
void OnKeyPressed(int keyCode)
{
 ...
}
void OnKeyPressed2(int keyCode)
{
 ...
}
...
Publisher publisher = new Publisher();
publisher.KeyPressed = OnKeyPressed;  //预订
publisher.KeyPressed += OnKeyPressed2; //预订另一个

  这样一个delegate不仅可以帮忙记录一个以上的subscribers,也可以简单的通过一行的调用语句来发布事件。其实编译器会为每一个delegate 生成一个相应的类来帮助处理这些工作,不过是作为一个只是编写应用系统的程序员是不必要去了解这些细节的,有兴趣的人可以去研究System.Delegate和System.MulticastDelegate 类。

  上面看到的结果应该已经是比较满意的,但是仍有改善空间。首先,因为一个delegate成员是public的,任何人都可以任意的直接接触,有失面向对象世界中的信息封装和隐藏(information encapsulation and hiding)的原则。所以在C#中又增加一个关键字“event”,用在放在声明一个delegate成员变量的前面,这样表示只有在声明这个delegate的类内部才可以直接对它进行subscriber 调用。

public delegate void KeyPressedDelegate(int keyCode);
class Publisher
{
 public event KeyPressedDelegate KeyPressed;
 ...
 void FireKeyPressedEvent(int KeyCode)
 {
  if (KeyPressed != null)
   //只有在Publisher才可以
   KeyPressed(keyCode);
 }
}

// outside of Publisher...
Publisher publisher = new Publisher();
// !!! 不允许 !!! 会编译错误 !!!
publisher.KeyPressed(100);

  接着,event delegate是以一个成员变量的方式存在,如果能以属性的方式让外界进行存取,不是更好吗。于是又增加了event Accessors。在C#语言中,是使用add和remove来封装实际的 += 和 -= 操作。如下:

class Publisher
{
 protected event KeyPressedDelegate m_KeyPressed;

 // event accessor。定义一个事件属性。
 public event KeyPressedDelegate KeyPressed
 {
  add
  {
   m_KeyPressed += value;
  }
  remove
  {
   m_KeyPressed -= value;
  }
 }
 void FireKeyPressedEvent(int KeyCode)
 {
  if (KeyPressed != null)
  m_KeyPressed(keyCode);
 }
}

  不管是事件变量或者是事件属性,对声明事件变量和属性的类的外部,只能对它做 += 和 -= 操作。这样可以加强它的安全性。当然event accessor只有add和remove操作,所以不管是任何人(包括声明该事件属性的类内部),也只能对事件属性做 += 和 -= 操作。
经过这样的改善,可以理论上更减弱publisher和subscriber之间的耦合力了。

  事件数据

  接下来我们谈一谈另一个在事件模型中的重要角色,就是在事件发布中被传递的“事件数据”。

  一个subscriber在接受同一种事件的时候,可能来自不同的publisher,所以自然地希望知道发出事件的人是谁,也就是在传递的参数当中,必需包含一个publisher 对象的引用。在Java中,推荐所有的事件数据类都继承java.util.EventObject 类。因为在生成一个EventObject 对象的时候,必需给一个event source 对象作为参数。然后可以通过EventObject的getSource()方法来取得这个对象。在EventObject里面,并没有包含其他任何事件数据,所以如果在事件的传递过程当中,有任何事件数据需要传递,就必需从EventObject 派生出一个新的子类出来。如下图:


  在.net当中也有一个相似的类叫System.EventArgs,但是这个类的内容是空的,如下:

public class EventArgs
{
 public static readonly EventArgs Empty;
 static EventArgs()
 {
  Empty = new EventArgs();
 }
 public EventArgs()
 {}
}

  .NET认为不一定所有的subscriber都对event source感兴趣,所以如果需要的话,就把event source当成是delegate方法的参数来传递好了。.NET定义了一个标准的delegate EventHandler,以下是它的签名(signature):

public delegate void EventHandler(object sender, EventArgs e);

  以后,只要你需要的delegate的签名与EventHandler相同的话,就直接用它了。这里所谓的签名相同,是指参数的类型和返回值的类型皆相同。

  Java和.NET都希望用户在定义的事件数据类的时候,尽可能的使用推荐的基类,因为这样在publisher对发出的事件数据内容有所变更或扩大的时候,对subscriber的冲击会比较小,这是由于多型(polymorphism)机制的帮助。

  结束语

  经过这番解析之后,应该能够比较清楚的了解到Java和.NET事件处理框架的设计思路,希望有助于读者更进一步理解其框架的形成过程。从语言的角度来看,.NET的确有一些针对性的改善和试图简化对事件的处理,Java则仍保有其一贯简约的风格。读者若有任何意见和指教,可以通过e-mail与我交流:bruceyou@sina100.com。

 
 
说明
:本教程来源互联网或网友上传或出版商,仅为学习研究或媒体推广,wanshiok.com不保证资料的完整性。
 

上一篇:JS中将字符串转为XML并读取对象的值  下一篇:Java多线程程序设计初步  线程的创建