Skip to content

名称

事件聚合器

目的

当客户端想要订阅事件时,具有大量对象的系统可能会比较复杂。客户端必须单独查找并注册每个对象,如果每个对象都有多个事件,则每个事件都需要单独的订阅。事件聚合器充当许多对象的单一事件源。它注册许多对象的所有事件,允许客户端仅向聚合器注册。

解释

真实世界例子

乔佛里国王坐在铁王座上,统治着维斯特洛的七个王国。他的大部分关键信息来自国王之手,第二指挥。国王之手有许多亲密的顾问,向他提供有关王国发生的事件的相关信息。

通俗的说

事件聚合器是一种事件中介器,它从多个源收集事件并将其传递给已注册的观察者。

编程示例

在我们的编程示例中,我们演示了事件聚合器模式的实现。有些对象是事件侦听器,有些是事件发送器,而事件聚合器两者都是。

java
public interface EventObserver {
  void onEvent(Event e);
}

public abstract class EventEmitter {

  private final Map<Event, List<EventObserver>> observerLists;

  public EventEmitter() {
    observerLists = new HashMap<>();
  }

  public final void registerObserver(EventObserver obs, Event e) {
    ...
  }

  protected void notifyObservers(Event e) {
    ...
  }
}

KingJoffrey正在侦听来自KingsHand的事件。

java
@Slf4j
public class KingJoffrey implements EventObserver {
  @Override
  public void onEvent(Event e) {
    LOGGER.info("Received event from the King's Hand: {}", e.toString());
  }
}

KingsHand 正在侦听来自下属 LordBaelish, LordVarys, 和 Scout的事件。 无论他从他们那里听到什么, 他都会交付给 KingJoffrey.

java
public class KingsHand extends EventEmitter implements EventObserver {

  public KingsHand() {
  }

  public KingsHand(EventObserver obs, Event e) {
    super(obs, e);
  }

  @Override
  public void onEvent(Event e) {
    notifyObservers(e);
  }
}

例如, LordVarys每个星期天找到一个叛徒并通知 KingsHand.

java
@Slf4j
public class LordVarys extends EventEmitter implements EventObserver {
  @Override
  public void timePasses(Weekday day) {
    if (day == Weekday.SATURDAY) {
      notifyObservers(Event.TRAITOR_DETECTED);
    }
  }
}

以下代码段演示了如何构造对象并将其连接在一起。

java
    var kingJoffrey = new KingJoffrey();

    var kingsHand = new KingsHand();
    kingsHand.registerObserver(kingJoffrey, Event.TRAITOR_DETECTED);
    kingsHand.registerObserver(kingJoffrey, Event.STARK_SIGHTED);
    kingsHand.registerObserver(kingJoffrey, Event.WARSHIPS_APPROACHING);
    kingsHand.registerObserver(kingJoffrey, Event.WHITE_WALKERS_SIGHTED);

    var varys = new LordVarys();
    varys.registerObserver(kingsHand, Event.TRAITOR_DETECTED);
    varys.registerObserver(kingsHand, Event.WHITE_WALKERS_SIGHTED);

    var scout = new Scout();
    scout.registerObserver(kingsHand, Event.WARSHIPS_APPROACHING);
    scout.registerObserver(varys, Event.WHITE_WALKERS_SIGHTED);

    var baelish = new LordBaelish(kingsHand, Event.STARK_SIGHTED);

    var emitters = List.of(
        kingsHand,
        baelish,
        varys,
        scout
    );

    Arrays.stream(Weekday.values())
        .<Consumer<? super EventEmitter>>map(day -> emitter -> emitter.timePasses(day))
        .forEachOrdered(emitters::forEach);

运行示例后的控制台输出。

18:21:52.955 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: Warships approaching
18:21:52.960 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: White walkers sighted
18:21:52.960 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: Stark sighted
18:21:52.960 [main] INFO com.iluwatar.event.aggregator.KingJoffrey - Received event from the King's Hand: Traitor detected

类图

alt text

适用性

在以下情况下使用事件聚合器模式

  • 当您有大量对象作为潜在事件源时,事件聚合器是一个不错的选择。您可以把注册逻辑集中到事件聚合器,而不是让观察者处理所有注册。除了简化注册之外,事件聚合器还简化了使用观察器时的内存管理问题。

相关模式

鸣谢