事件聚合器

将来自多个对象的事件汇集到单个对象中,以简化客户端的注册。

2004年9月29日

这是我在2000年代中期进行的《进一步的企业应用程序架构开发》写作的一部分。不幸的是,此后太多其他事情吸引了我的注意力,所以我没有时间进一步研究它们,而且在可预见的未来我也没有看到太多时间。因此,这些材料非常草稿形式,在我有时间再次处理它们之前,我不会进行任何更正或更新。

当客户端想要订阅事件时,具有大量对象的系统会导致复杂性。客户端必须分别找到并注册每个对象,如果每个对象具有多个事件,则每个事件都需要单独的订阅。

一个事件聚合器充当许多对象的单个事件源。它注册所有对象的事件,允许客户端仅向聚合器注册。

工作原理

一个事件聚合器是一个简单的间接元素。在最简单的形式中,您让它注册您感兴趣的所有源对象,并让所有目标对象注册到事件聚合器。当事件聚合器接收到来自源对象的任何事件时,它会将该事件传播到目标对象。

最简单的事件聚合器将来自多个对象的事件聚合到自身,并将相同的事件传递到其观察者。一个事件聚合器还可以概括事件,将特定于源对象的事件转换为更通用的事件。这样,聚合器的观察者就不需要注册那么多的单个事件类型。这简化了观察者的注册过程,但代价是会收到可能对观察者没有实质影响的事件通知。

由于事件聚合器基于观察者,因此重要的是要考虑所有观察者中的危险区域

何时使用它

事件聚合器是您有许多潜在事件源对象时的不错选择。与其让观察者处理向所有对象注册,不如将注册逻辑集中到事件聚合器。除了简化注册之外,事件聚合器还简化了使用观察者时的内存管理问题。

进一步阅读

您可以将事件聚合器视为一种特定形式的外观,它只关注观察者关系。

示例:观察我们的顾问(C#)

当有人加入 Thoughtworks 时,他们会立即获得一部手机。老笑话是,这是因为 Roy(首席执行官)希望能够在任何时候打电话给你。所以在这里,我设想了一个简单的应用程序,它告诉 Roy 他所有顾问的位置以及他们是否可以接听电话。

图 1:顾问类

为了捕捉这一点,我们有顾问对象,如图 1所示。当顾问四处移动时,我们通过关于这些移动的消息来捕捉这一点,这些消息表明哪个顾问移动了以及他们现在在哪里。

当系统接收到这些消息时,它们会被传递给顾问对象以处理它们并更新顾问的状态。更新当前位置相当明显,更新可用性有一些轻微的计算 - 本质上每个顾问都有一个家,并且可以选择他们在家里是否可用。(假设他们在路上时总是会受到 Roy 的电话支配。)

class Consultant...

  public bool IsAvailable {
    get {return IsAvailableAt(_current_location);}
  }
  private bool IsAvailableAt(string location) {
    return (location == Home) ? _availableAtHome : true;
  }

因此,当顾问处理移动消息时,它必须担心可能发生变化的两件事 - 当前位置和可用性。它为每个信号发出一个单独的事件。

class Consultant...

  public void HandleMovement(MsgMovement movement) {
    if (_current_location != movement.Place) {
      if (LocationChanged != null) LocationChanged(this, EventArgs.Empty);
      if (IsAvailableAt(movement.Place) != IsAvailable) {
        if (AvailabilityChanged != null) AvailabilityChanged(this, EventArgs.Empty);
      }
      _current_location = movement.Place;
    }
  }

此应用程序的屏幕可能会选择为所有顾问注册这些事件,但在这里我将使用一个事件聚合器。该事件聚合器通过注册所有顾问的事件来监控顾问。

class EventAggregator...

  public void Listen (Consultant subject) {
    subject.LocationChanged += new Consultant.ConsultantEventHandler(HandleConsultantLocationChanged);
    subject.AvailabilityChanged += new Consultant.ConsultantEventHandler(HandleConsultantAvailabilityChanged);
  }

当它接收到事件时,它会发出两个事件,一个特定于更改类型的事件,另一个是仅指示已发生更改的通用事件。这允许客户端根据自己的需要注册到他们想要的粒度。

class EventAggregator...

  private void HandleConsultantLocationChanged (Consultant consultant, EventArgs args) {
    if (ConsultantLocationChanged != null) ConsultantLocationChanged(consultant, args); 
    if (ConsultantChanged != null) ConsultantChanged(consultant, args);
  }
  private void HandleConsultantAvailabilityChanged (Consultant consultant, EventArgs args) {
    if (ConsultantAvailabilityChanged != null) ConsultantAvailabilityChanged(consultant, args);
    if (ConsultantChanged != null) ConsultantChanged(consultant, args);
  }
  public event Consultant.ConsultantEventHandler ConsultantChanged;
  public event Consultant.ConsultantEventHandler ConsultantLocationChanged;
  public event Consultant.ConsultantEventHandler ConsultantAvailabilityChanged;

大多数情况下,我建议只使用一个粗粒度的事件。但是,这很好地说明了如何通过提供各种不同的反应来响应传入事件,具体取决于客户的需求。这也允许事件聚合器充当适配器以及外观。