TAG

首都機能移轉 (2) 歌詞 (2) 靠北文 (40) 戲言 (30) 糟糕 (7) ACG (23) Assembly (2) Boost (2) C (31) C++ (69) CMake (4) CSIE (67) Debian (34) Design_Pattern (2) Django (1) Eclipse (1) en_US (13) FFmpeg (3) FoolproofProject (26) FreeBSD (2) Git (4) GNU_Linux (65) IDE (5) Java (11) JavaScript (19) KDE (15) Khopper (16) KomiX (3) Kubuntu (18) Life (1) Lighttpd (2) Mac_OS_X (2) Opera (1) PHP (2) PicKing (2) Programing (21) Prolog (1) Python (7) QSnapshot (2) Qt (30) Qt_Jambi (1) Regular_Expression (1) Shell_Script (7) Talk (98) VirtualBox (7) Visual_Studio (13) Windows (18) zh_TW (36)

2011年4月1日 星期五

Another Observer Pattern implementation in Java

由於實在不喜歡 Swing 的 listener 方式,因此我嘗試使用 Java 內建的 ObserverObservable 來套用 Observer Pattern,但它比我想像中地還不靈活,原因有三:
  1. 必須要呼叫 Observable.setChanged 才會讓 Observable.notifyObservers 生效,而 Observable.setChanged 是 protected,這意味著你至少必須繼承 Observable 類別,這對於不允許多重繼承的 Java 來說很要命,因為 host class 很有可能來自另一繼承體系。
  2. 你沒辦法透過 composition 繞過這個限制,因為 Observer.update 帶有一個型態為 Observable 的參數,語義上這個參數必須要是發出事件的物件,但若是使用 composition 持有 Observable 物件,Observer 收到的參數就會跟真正發出事件的物件無關,這會破壞這組範式的假設。
  3. 你有可能需要在同一個類別裡發送不同的事件,但使用繼承的做法你只能使用一種通知。如果要達到分派不同事件的目的,就必須要在那唯一的一個 Object 參數上作手腳。
為此我參考了之前設計的 Observer Pattern 大綱,重新造了一個輪子:
using java.util.ArrayList;

class Signal {

    public static interface Slot {
        void call( Object sender, Object ... args );
    }

    public Signal( Object sender ) {
        this.sender_ = sender;
        this.slots_ = new ArrayList< Slot >();
    }

    public synchronized Boolean connect( Slot slot ) {
        if( this.slots_.indexOf( slot ) >= 0 ) {
            return false;
        }
        this.slots_.add( slot );
        return true;
    }

    public synchronized void disconnect() {
        this.slots_.clear();
    }

    public synchronized Boolean disconnect( Slot slot ) {
        return this.slots_.remove( slot );
    }

    public synchronized void emit( Object ... args ) {
        for( Slot slot : this.slots_ ) {
            slot.call( this.sender_, args );
        }
    }

    private Object sender_;
    private ArrayList< Slot > slots_;

}
其實 Slot 沒必要是 Signal 的從屬類別,只是它也只會給 Signal 使用,就放在裡面了。使用上只要實作出 Slot 介面,再丢給 Signal.connect 就可以註冊,呼叫 Signal.emit 就可以發出通知:
class A {

    private Signal event1_;
    private Signal event2_;

    public A() {
        this.event1_ = new Signal( this );
        this.event2_ = new Signal( this );
    }

    public Signal onEvent1() {
        return this.event1_;
    }

    public Signal onEvent2() {
        return this.event2_;
    }

    public void trigger() {
        this.event1_.emit( "First event happened" );
        this.event2_.emit( 2, "Multiple args" );
    }

}

// ... client code

A a = new A();
a.onEvent1().connect( new Signal.Slot() {
    void call( Object sender, Object ... args ) {
        System.err.printf( "$s message : %s", sender, args[0] );
    }
} );
a.onEvent2().connect( new Signal.Slot() {
    void call( Object sender, Object ... args ) {
        System.err.printf( "$s message : %d, %s", sender, args.length, args.[1] );
    }
} );
a.trigger();
沒有用上 generic 是因為 ... 我認為它在這個問題上幫不上什麼忙。利用不定引數則是為了消去要為不同的參數組而另訂類別的困擾。

沒有留言:

張貼留言