2014年5月16日 星期五

HandlerManager 與兩個 event package

我大概是當完兵之後才開始用 HandlerManager 作 event bus,目的當然是降低耦合度。起手 reference 是 tkcn 的這篇《利用 HandlerManager 實作共用的 Event Bus》,然後一直以來就爽爽用,沒出啥問題、也沒想過會出問題。今天在 review 別人的 code 才知道有 SimpleEventBus,然後想知道這兩個到底有什麼差別、該用哪一個比較好?沒想到才剛打開 HandlerManager 的 source code,一開頭的 javadoc 就開始噴血:

application developers are strongly discouraged from using a HandlerManager instance as a global event dispatch mechanism.

WTF?不但不建議,而且是強烈不建議?GWT MVP 的文件都還是教用 HandlerManager 阿?

tkcn 建議我去找這段 javadoc 是什麼時候改的,於是考古了一下,發現 HandlerManager 是 2008.11.18 建的,然後上頭那段恐嚇文字是 2010.12.03 才加上去的(HandlerManager log)。

這下子就跑出另一個問題了:「為什麼?」

HandlerManager 裡頭還不是用了一個繼承 SimpleEventBusBus 然後以此 delegate…… 等等,有兩個 SimpleEventBus

一個是來自 com.google.gwt.event.shared(以下簡稱 gwt.event),一個是來自 com.google.web.bindery.event.shared(以下簡稱 bindery.event)。 gwt.eventSimpleEventBus 還是有一個 bindery.eventSimpleEventBus 的 instance(變數名稱為 real)。以 fireEvent() 來說,如果要 fire 的 event 是 bindery.eventEvent 就用 real 來處理,如果要 fire 的 event 是 GwtEvent(在 gwt.event 下),則改呼叫 (gwt.eventEventBus.castFireEvent()castFireEvent() 的程式碼感覺單純

protected void castFireEvent(GwtEvent<?> event) {
    try {
        fireEvent((Event<?>) event);
    } catch (com.google.web.bindery.event.shared.UmbrellaException e) {
        throw new UmbrellaException(e.getCauses());
    }
}

GwtEvent 一樣也是繼承 Event,帳面上看起來根本不會有炸 exception 的可能?而且最後還是呼叫 bindery.event 的那個 fireEvent(),也就是說,不管怎樣最後處理的都是那個 bindery.eventSimpleEventBus(變數 real)。這一切到底有什麼意義?

gwt.eventSimpleEventBus 看起來:

Wraps {com.google.web.bindery.event.shared.SimpleEventBus} for legacy compatibility.

bindery.event 下的 class 應該都是歷史的眼淚。甚至 gwt.eventEventBus.fireEvent(Event<?>) 還會炸有下列訊息的 UnsupportedOperationException

Subclass responsibility. This class is a legacy wrapper for com.google.web.bindery.event.shared.EventBus. Use that directly, or try com.google.gwt.event.shared.SimpleEventBus

先面有提到,SimpleEventBus 有 override 掉用,所以用起來也沒事情。這樣看起來 gwt.event 底下才是未來,bindery.event 只是忘記還沒法拋棄的舊情人。正當一切都覺得很踏實的時候,GwtEvent 的 javadoc 馬上來了一記回馬槍:

There is no need for an application's custom event types to extend GwtEvent. Prefer {@link Event} instead.

既然建議改用(bindery.eventEvent,那 gwt.event 的那個 EventBus.fireEvent(Event<?>) 為什麼又要預設這種行為?你為什麼要讓他留在 bindery.event 的 package 底下?

整理到這邊,我就放棄了 [死]。

不負責任的結論

截至目前(2.6.1)的版本看來,要不要繼續用 HandlerManagerGwtEvent 還是 bindery.event 下的東西,學理上都不會出事、效果也幾乎一模一樣。至於 package 分割的方式、未來這個部份會不會有所改變(謎之聲:應該不會),那等炸了再說吧……

說不定就算寫信去問 GWT 委員會,他們也都忘記有這件事了……

順便佔篇幅的附錄

bindery.eventEvent.TypehashCode 是這樣來的:

private static int nextHashCode;
private final int index;

public Type() {
    index = ++nextHashCode;
}

public final int hashCode() {
    return index;
}

實際上效率應該會不錯,無論是用它當 key 取出對應的 handler 們、還是 new 一個新的 type、單純看 event / handler 的實做 code 也都很 Java。不過底層的 code 用 Java 的角度大概會被詰譙到死(連個 synchronized 都沒有是哪招?)。只能說 GWT 寫起來還是並不完全等同 Java,在看不到的底層就可以用、也應該要用 JS 的思維亂來 XDDD

沒有留言:

張貼留言