事件聚合器
将来自多个对象的事件汇集到单个对象中,以简化客户端的注册。
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;
大多数情况下,我建议只使用一个粗粒度的事件。但是,这很好地说明了如何通过提供各种不同的反应来响应传入事件,具体取决于客户的需求。这也允许事件聚合器充当适配器以及外观。