科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件探讨与比较Java和.NET的事件处理框架

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

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

事件驱动模型是软件系统平台中的一个重要区域

作者:佚名 来源:程序员杂志 2007年11月4日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
事件的预订和发布

  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之间的耦合力了。
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章