blog 版:
http://blog.dontcareabout.us/2014/05/handlermanager-event-package.html
BBS 版(大抵上)用 markdown 語法撰寫
______________________________________________________________________
我大概是當完兵之後才開始用 `HandlerManager` 作 event bus,
目的當然是降低耦合度。
起手 reference 是 tkcn 的這篇
《[利用 HandlerManager 實作共用的 Event Bus][tkcn ref]》,
然後一直以來就爽爽用,沒出啥問題、也沒想過會出問題。
今天在 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 ref]: http://tkcnandy.blogspot.tw/2009/12/
handlermanager-event-bus.html
tkcn 建議我去找這段 javadoc 是什麼時候改的,
於是考古了一下,發現 `HandlerManager` 是 2008.11.18 建的,
然後上頭那段恐嚇文字是 2010.12.03 才加上去的
([HandlerManager log])。
[HandlerManager log]: https://gwt.googlesource.com/gwt/+/
58c04717a6ec18dd65adda0e3b6b6a624a3b7ca5%5E%21/#F0
這下子就跑出另一個問題了:「為什麼?」
`HandlerManager` 裡頭還不是用了一個繼承 `SimpleEventBus` 的 `Bus`
然後以此 delegate...... 等等,有兩個 `SimpleEventBus`?
一個是來自 `com.google.gwt.event.shared`(以下簡稱 `gwt.event`),
一個是來自 `com.google.web.bindery.event.shared`
(以下簡稱 `bindery.event`)。
`gwt.event` 的 `SimpleEventBus` 還是有一個
`bindery.event` 的 `SimpleEventBus` 的 instance(變數名稱為 `real`)。
以 `fireEvent()` 來說,如果要 fire 的 event
是 `bindery.event` 的 `Event` 就用 `real` 來處理,
如果要 fire 的 event 是 `GwtEvent`(在 `gwt.event` 下),
則改呼叫 (`gwt.event`)`EventBus.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.event` 的 `SimpleEventBus`(變數 `real`)。
這一切到底有什麼意義?
從 `gwt.event` 的 `SimpleEventBus` 看起來:
Wraps {com.google.web.bindery.event.shared.SimpleEventBus} for
legacy compatibility.
`bindery.event` 下的 class 應該都是歷史的眼淚。
甚至 `gwt.event` 的 `EventBus.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.event`)`Event`,
那 `gwt.event` 的那個 `EventBus.fireEvent(Event<?>)`
為什麼又要預設這種行為?
你為什麼要讓他留在 `bindery.event` 的 package 底下?
整理到這邊,我就放棄了 [死]。
### 不負責任的結論 ###
截至目前(2.6.1)的版本看來,
要不要繼續用 `HandlerManager`、`GwtEvent`
還是 `bindery.event` 下的東西,
學理上都不會出事、效果也幾乎一模一樣。
至於 package 分割的方式、未來這個部份會不會有所改變
(謎之聲:應該不會),那等炸了再說吧......
說不定就算寫信去問 GWT 委員會,他們也都忘記有這件事了......
### 順便佔篇幅的附錄 ###
`bindery.event` 的 `Event.Type` 的 `hashCode` 是這樣來的:
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