Java:实现将不同实例重定向到不同订阅者的观察者模式的最佳方法是什么?

Java: Whats the best way to implement an observer pattern which redirects different instances to different subscriber?

我有一个 class 带有不同子 class 的记录(RecordA、RecordB 等等)。 我有一个 class 接收一些对象并创建那些不同的记录。 我想要类似观察者模式的东西,但观察者只对某些子类型感兴趣。

例如我有一个观察者想要接收 RecordA 和 RecordC 的所有实例。 另一个观察者想要接收 RecordB 的所有实例。

做这样的事情最好的方法是什么?

有两种基本方法:

  • 在观察者中过滤:每个观察者都会收到有关所有记录的通知。然后观察者过滤掉感兴趣的记录并丢弃其他通知。当通知是本地的(进程间通信)时,这是一种简单易行的方法。
  • 在observable中过滤:每个观察者在注册时定义他想要接收的通知(记录类型)。然后,仅当记录与订阅匹配时,observable 才会过滤并通知观察者。这有点复杂,需要对订阅选项进行簿记,但会为非本地(进程内)通信支付费用。

代码示例:

Observer 用于通用类型的消息 M:

public interface Observer<M> {
    void notify(M message);
}

支持两种变体的Observable:在可观察对象中进行过滤和不进行过滤(然后可以在观察者中完成):

public interface Observable<M> {

    void subscribe(Observer<M> observer, Predicate<M> filter);

    default void subscribe(Observer<M> observer) {
        subscribe(observer, message -> true);
    }
}

一个简单的通用默认实现:

public class SimpleObservable<M> implements Observable<M> {

    private Map<Observer<M>, Predicate<M>> subscriptions = new HashMap<>();

    @Override
    public void subscribe(Observer<M> observer, Predicate<M> filter) {
        subscriptions.put(observer, filter);
    }

    public void notifyObservers(M message) {
        subscriptions.forEach((observer, filter) -> {
            if (filter.test(message)) observer.notify(message);
        });
    }
}

一切都在行动:date/time 消息的可观察者(Temporal 的实现),以及四个对不同 date/time 类 感兴趣的不同观察者,其中两个其中一个对订阅进行过滤,一个进行自己的过滤,一个只消耗所有事件:

Observer<Temporal> timeObserver = message -> System.out.println("The time is: " + message);
Observer<Temporal> dateObserver = message -> System.out.println("The date is: " + message);
Observer<Temporal> dateTimeObserver = message -> {
    if (message instanceof LocalDateTime) {
        System.out.println("The date/time is: " + message);
    }
};
Observer<Temporal> generalObserver = message -> System.out.println("Now is: " + message);

SimpleObservable<Temporal> clock = new SimpleObservable<>();
clock.subscribe(timeObserver, temporal -> temporal instanceof LocalTime);
clock.subscribe(dateObserver, temporal -> temporal.getClass().getSimpleName().equals("LocalDate"));
clock.subscribe(dateTimeObserver); // does its own filtering
clock.subscribe(generalObserver); // no filtering

clock.notifyObservers(LocalDate.now());
clock.notifyObservers(LocalTime.now());
clock.notifyObservers(LocalDateTime.now());
clock.notifyObservers(OffsetDateTime.now());

输出:

Now is: 2021-07-13
The date is: 2021-07-13
The time is: 11:37:05.997223
Now is: 11:37:05.997223
Now is: 2021-07-13T11:37:05.997319
The date/time is: 2021-07-13T11:37:05.997319
Now is: 2021-07-13T11:37:05.997626+02:00