淺析Swing線程中的LookupEvent
所有前面的這些解決方案都存在一個共同的致命缺陷--企圖在持續(xù)地改變線程的同時表示一個任務(wù)的功能集。但是改變線程需要異步的模型,而線程異步地處理Runnable。問題的部分原因是我們在企圖在一個異步的線程模型之上實現(xiàn)一個同步的模型。這是所有Runnable之間的鏈和依賴,執(zhí)行順序和內(nèi)部類scooping問題的根源。如果我們可以構(gòu)建真正的異步,我們就可以解決我們的問題并極大地簡化Swing線程。
讓我們考慮一下像Java消息服務(wù)(JMS)這樣的基于消息的系統(tǒng),因為它們提供了在異步環(huán)境中功能組件之間的松散耦合。消息系統(tǒng)觸發(fā)異步事件,正如在Enterprise Integration Patterns 中描述的。感興趣的參與者監(jiān)聽該事件,并對事件做成響應(yīng)--通常通過執(zhí)行它們自己的一些代碼。結(jié)果是一組模塊化的,松散耦合的組件,組件可以添加到或者從系統(tǒng)中去除而不影響到其它組件。更重要的,組件之間的依賴被最小化了,而每一個組件都是良好定義的和封裝的--每一個都僅對自己的工作負(fù)責(zé)。它們簡單地觸發(fā)消息,其它一些組件將響應(yīng)這個消息,并對其它組件觸發(fā)的消息進(jìn)行響應(yīng)。
現(xiàn)在,我們先忽略線程問題,將組件解耦并移植到異步環(huán)境中。在我們解決了異步問題后,我們將回過頭來看看線程問題。正如我們所將要看到的,那時解決這個問題將非常容易。
讓我們還拿前面引入的例子,并把它移植到基于事件的模型。首先,我們把lookup調(diào)用抽象到一個叫LookupManager的類中。這將允許我們將所有UI類中的數(shù)據(jù)庫邏輯移出,并最終允許我們完全將這兩者脫耦。下面是LookupManager類的代碼:
- classLookupManager{
- privateString[]lookup(Stringtext){
- String[]results=...
- //databaselookupcode
- returnresults
- }
- }
現(xiàn)在我們開始向異步模型轉(zhuǎn)換。為了使這個調(diào)用異步化,我們需要抽象調(diào)用的返回。換句話,方法不能返回任何值。我們將以分辨什么相關(guān)的動作是其它類所希望知道的開始。在我們這個例子中最明顯的事件是搜索結(jié)束事件。所以讓我們創(chuàng)建一個監(jiān)聽器接口來響應(yīng)這些事件。該接口含有單個方法lookupCompleted()。下面是接口的定義:
- interfaceLookupListener{
- publicvoidlookupCompleted(Iteratorresults);
- }
遵守Java的標(biāo)準(zhǔn),我們創(chuàng)建另外一個稱作LookupEvent的類包含結(jié)果字串?dāng)?shù)組,而不是到處直接傳遞字串?dāng)?shù)組。這將允許我們在不改變LookupListener接口的情況下傳遞其它信息。例如,我們可以在LookupEvent中同時包括查找的字串和結(jié)果。下面是LookupEvent類:
- publicclassLookupEvent{
- StringsearchText;
- String[]results;
- publicLookupEvent(StringsearchText){
- this.searchText=searchText;
- }
- publicLookupEvent(StringsearchText,
- String[]results){
- this.searchText=searchText;
- this.results=results;
- }
- publicStringgetSearchText(){
- returnsearchText;
- }
- publicString[]getResults(){
- returnresults;
- }
- }
注意LookupEvent類是不可變的。這是很重要的,因為我們并不知道在傳遞過程中誰將處理這些事件。除非我們創(chuàng)建事件的保護(hù)拷貝來傳遞給每一個監(jiān)聽者,我們需要把事件做成不可變的。如果不這樣,一個監(jiān)聽者可能會無意或者惡意地修訂事件對象,并破壞系統(tǒng)。
現(xiàn)在我們需要在LookupManager上調(diào)用lookupComplete()事件。我們首先要在LookupManager上添加一個LookupListener的集合:
- publicvoidaddLookupListener(LookupListenerlistener){
- listeners.add(listener);
- }
- publicvoidremoveLookupListener(LookupListenerlistener){
- listeners.remove(listener);
- }
當(dāng)動作發(fā)生時,我們需要調(diào)用監(jiān)聽者的代碼。在我們的例子中,我們將在查找返回時觸發(fā)一個lookupCompleted()事件。這意味著在監(jiān)聽者集合上迭代,并使用一個LookupEvent事件對象調(diào)用它們的lookupCompleted()方法。
我喜歡把這些代碼析取到一個獨立的方法fire[event-method-name] ,其中構(gòu)造一個事件對象,在監(jiān)聽器集合上迭代,并調(diào)用每一個監(jiān)聽器上的適當(dāng)?shù)姆椒ā_@有助于隔離主要邏輯代碼和調(diào)用監(jiān)聽器的代碼。下面是我們的fireLookupCompleted方法:
- privatevoidfireLookupCompleted(StringsearchText,
- String[]results){
- LookupEventevent=
- newLookupEvent(searchText,results);
- Iteratoriter=
- newArrayList(listeners).iterator();
- while(iter.hasNext()){
- LookupListenerlistener=
- (LookupListener)iter.next();
- listener.lookupCompleted(event);
- }
- }
我們知道這將在非Swing線程中調(diào)用,因為該事件是直接在LookupManager中觸發(fā)的,這將不是在Swing線程中執(zhí)行。因為所有的代碼功能上都是異步的(我們不必等待監(jiān)聽器方法允許結(jié)束后才調(diào)用其它代碼),我們可以通過SwingUtilities.invokeLater()將這些代碼改道到Swing線程。下面是新的方法,傳入一個匿名Runnable到SwingUtilities.invokeLater():
- publicvoidlookupCompleted(finalLookupEvente){
- //noticethethreading
- SwingUtilities.invokeLater(
- newRunnable(){
- publicvoidrun(){
- outputTA.setText("");
- Strin
【編輯推薦】