使用函式式實現觀察者模式模式

banq發表於2019-01-30

觀察者模式肯定是最常見和最廣泛使用的模式之一。其目的是允許在某個事件發生時通知一個或多個物件並相應地採取行動。這種模式的主要抽象是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!" );




 

相關文章