使用函数式实现观察者模式模式

19-01-30 banq
                   

观察者模式肯定是最常见和最广泛使用的模式之一。其目的是允许在某个事件发生时通知一个或多个对象并相应地采取行动。这种模式的主要抽象是Listener接口:

interface Listener {
    void onEvent(Object event);
}

当on对象想要在事件发生时得到通知,或者要监听事件时,它只需实现此接口并在onEvent()方法的主体中编码它如何对事件的到达作出反应。对应的是一个Observable对象,或者换句话说,一个对象通过在相关事件发生时向它们发送事件来通知其注册的侦听器。

public class Observable {
    private final Map<Object, Listener> listeners = new ConcurrentHashMap<>();
 
    public void register(Object key, Listener listener) {
        listeners.put(key, listener);
    }
 
    public void unregister(Object key) {
        listeners.remove(key);
    }
 
    public void sendEvent(Object event) {
        for (Listener listener : listeners.values()) {
            listener.onEvent( event );
        }
    }
}

在引入lambdas之前,在这个Observable上注册Listener的两种典型方法是通过匿名内部类:

public class Observer1 {
    Observer1(Observable observable) {
        observable.register( this, new Listener() {
            @Override
            public void onEvent( Object event ) {
                System.out.println(event);
            }
        } );
    }
}

或使您的对象直接实现Listener接口。

public class Observer2 implements Listener {
    Observer2(Observable observable) {
        observable.register( this, this );
    }
    @Override
    public void onEvent( Object event ) {
        System.out.println(event);
    }
}

这两个观察者都可以以相同的方式使用,当Observable发送一个事件时,它将被广播到:

Observable observable = new Observable();
new Observer1( observable );
new Observer2( observable );
observable.sendEvent( "Hello World!" );

然而,这两个解决方案再一次揭示了GoF模式最大部分的常见问题:它们必须将动词以及事件到达时要采取的行动转换为名词、类别、匿名或不包装这些行为。为了利用Java 8的新功能特性,首先要注意的是我们上面定义的Listener接口在语义上等同于Consumer:

public class Observable {
    private final Map<Object, Consumer<Object>> listeners = new ConcurrentHashMap<>();
 
    public void register(Object key, Consumer<Object> listener) {
        listeners.put(key, listener);
    }
 
    public void unregister(Object key) {
        listeners.remove(key);
    }
 
    public void sendEvent(Object event) {
        listeners.values().forEach( listener -> listener.accept( event ) );
    }
}

此外,不再需要使用特定类实现Listener,并且可以使用lambda表达式对事件到达的反应进行编码,或者在这种情况下也使用更简洁的方法引用进行编码。

Observable observable = new Observable();
observable.register( "key1", e -> System.out.println(e) );
observable.register( "key2", System.out::println );
observable.sendEvent( "Hello World!" );