2010-04-18 108 views
1

我在所有處理由事件總線類觸發的事件的觀察者類中都有類似於此的代碼。 正如您所看到的,有很多instanceof檢查來選擇適當處理事件所需的操作路徑,並且我想知道是否可以更乾淨地完成此操作,從而消除了instanceof測試?乾淨地處理事件

@Override 
public void handleEvent(Event event) { 
    if (event instanceof DownloadStartedEvent) { 
     DownloadStartedEvent dsEvent = (DownloadStartedEvent)event; 
     dsEvent.getDownloadCandidateItem().setState(new BusyDownloadingState()); 
    } else if (event instanceof DownloadCompletedEvent) { 
     DownloadCompletedEvent dcEvent = (DownloadCompletedEvent)event; 
     dcEvent.getDownloadCandidateItem().setState(new FinishedDownloadingState()); 
     DownloadCandidate downloadCandidate = dcEvent.getDownloadCandidateItem(). getDownloadCandidate(); 
     if (downloadCandidate.isComplete()) { 
      // start extracting 
     } 
    } else if (event instanceof DownloadFailedEvent) { 
     DownloadFailedEvent dfEvent = (DownloadFailedEvent)event; 
     dfEvent.getDownloadCandidateItem().setState(new FailedDownloadingState()); 
    } 
} 
+1

這是一個乾淨的,面向對象的方式來做到這一點。其他方式將使用切換瀑布事件的參數..你爲什麼認爲這不明確? – Jack 2010-04-18 15:44:24

+0

@Jack如果您希望稍後添加更多活動,那麼這是一個維護噩夢:總會有一個您忘記檢查的活動。 – jqno 2010-04-18 15:53:00

+0

如果你有很多事件,那麼它將會成爲維護噩夢,直到​​你不給事件祖先類中的常見行爲,而不是將它們全部分開。實際上,當number_events> a_considerable_amount時,你將發誓在任何情況下:/ – Jack 2010-04-18 15:56:13

回答

6

您可以通過添加監聽器依次在每個事件消除事件。例如考慮

public void handleStartedEvent(DownloadStartedEvent ev) { ... } 
public void handleCompletedEvent(DownloadCompletedEvent ev) { ... } 
public void handleFailedEvent(DownloadFailedEvent ev) { ... } 

您也可以考慮合併完成/失敗成一個單一的事件,因爲它看起來像你已經有一個isCompleted方法。您可以處理CompleteEvent並檢查是否成功。如果成功,你可以開始提取,否則你可以設置你的失敗狀態。

我的另一個想法是爲什麼你設置一個新的對象作爲狀態指示器。使用常量/枚舉值可能會更好嗎?

1

是的,假設您控制了Event類和/或其子類。您可以將handle抽象方法添加到您的Event類中,並將每個子類的特定代碼移動到該方法中。

它應該是這樣的:

abstract class Event { 
    //... 
    public abstract void handle(); 
} 

class DownloadStartedEvent extends Event { 
    //... 
    @Override 
    public void handle() { 
    getDownloadCandidateItem().setState(new BusyDownloadingState()); 
    } 
} 

// The same for the other classes 

在調用代碼,只需寫:

@Override 
public void handleEvent(Event event) { 
    event.handle(); 
} 
+0

這將工作,但它會以某種方式破壞封裝,事件不應該嘗試處理自己:) – Jack 2010-04-18 15:48:40

+0

@Jack取決於情況,我認爲。如果你真的不喜歡它,你可以添加一個處理程序類的並行樹並將事件委託給它。 – jqno 2010-04-18 15:55:03

1

您可以使用註釋提出一個更清潔的解決方案。你需要定義@EventHandler@HandlesEvent,然後用它喜歡:

@EventHandler 
public class MyEventHandler { 

    @HandlesEvent(DownloadStartedEvent.class) 
    public void handleDownloadStartedEvent(DownloadStartedEvent dse) { 
     dsEvent.getDownloadCandidateItem().setState(new BusyDownloadingState()); 
    } 

    @HandlesEvent(DownloadCompletedEvent.class) 
    public void handleDownloadCompletedEvent(DownloadComletedEvent dse) { 
     dcEvent.getDownloadCandidateItem().setState(new FinishedDownloadingState()); 
     DownloadCandidate downloadCandidate = dcEvent.getDownloadCandidateItem(). getDownloadCandidate(); 
     if (downloadCandidate.isComplete()) { 
      // start extracting 
     } 
    } 


    // etc. 

} 

當然,你需要額外的代碼來註冊這個類,它需要使用反射來檢查哪些方法處理該事件。然後你的觀察者與這個註冊服務器進行交互,後者又與上面的@EventHandler註釋類進行交互。

0

如果你需要做的只是實例化「狀態」,比如BusyDownloadedStateFinishedDownloadedState,你可以給你的Event類指定哪一個屬性是必須根據事件類型設置的狀態。

如果您需要有既簡單事件和複雜事件,你可以有兩種東西的組合:

class Event 
{ 
    State state; 
    boolean isSimple; 
} 

public void handleEvent(Event event) 
{ 
    if (event.isSimple) 
    setState(event.state) 
    else 
    { 
    if (event instanceof WhateverEvent) 
     // special handling 
    } 
} 
+0

這樣做的缺點是,如果有很多特殊情況,最終會得到一個更復雜的原始代碼變體。 – jqno 2010-04-18 15:56:45

+0

是的,你應該準確地選擇這個東西的粒度..當然,我們不知道他需要多少事件或種類的事件來爲他的案例提供完美的解決方案..實際上,在類似的情況下,我會結束只要我意識到規範需要不同的東西就可以重構整個事物。 – Jack 2010-04-18 16:03:28

0

提高,而不必爲每個事件單獨的方法對瑞安的回答,您可以使用泛型。然後你可以這樣寫:

public void registerListeners() { 

eventSource.addListener(DownloadStartedEvent.class, new EventHandler<DownloadStartedEvent>() { 
    public void handleEvent(DownloadStartedEvent event) { 
     dsEvent.getDownloadCandidateItem().setState(new BusyDownloadingState()); 
    } 
}); 

eventSource.addListener(DownloadCompletedEvent.class, new EventHandler<DownloadCompletedEvent>() { 
    public void handleEvent(DownloadCompletedEvent event) { 
     dcEvent.getDownloadCandidateItem().setState(new FinishedDownloadingState()); 
     DownloadCandidate downloadCandidate = dcEvent.getDownloadCandidateItem(). getDownloadCandidate(); 
     if (downloadCandidate.isComplete()) { 
      // start extracting 
     } 
    } 
}); 

eventSource.addListener(DownloadFailedEvent.class, new EventHandler<DownloadFailedEvent>() { 
    public void handleEvent(DownloadFailedEvent event) { 
     dfEvent.getDownloadCandidateItem().setState(new FailedDownloadingState()); 
    } 
}); 
} 

我離開了addListener()的實現,作爲讀者的練習。

0

如何:

public void handleEvent(Event e) { 
    // Not interested 
} 


public void handleEvent(DownloadStartedEvent dsEvent) { 
    dsEvent.getDownloadCandidateItem().setState(new BusyDownloadingState()); 
} 


public void handleEvent(DownloadCompletedEvent dcEvent) { 
    dcEvent.getDownloadCandidateItem().setState(new FinishedDownloadingState()); 
    DownloadCandidate downloadCandidate = dcEvent.getDownloadCandidateItem().getDownloadCandidate(); 
    if (downloadCandidate.isComplete()) { 
     // start extracting 
    } 
} 

public void handleEvent(DownloadFailedEvent dfEvent) { 
    dfEvent.getDownloadCandidateItem().setState(new FailedDownloadingState()); 
} 
相關問題