2009-06-28 62 views
13

最近,我正在與另一位程序員討論重構一個充滿「if」語句的巨大(1000行)方法的最佳方式。責任鏈與類列表的優點是什麼?

該代碼是用Java編寫的,但我想這個問題也可能發生在其他語言中,例如C#。

爲了解決這個問題,他建議使用責任鏈模式。 他建議有一個「Handler」類的基礎。然後,「Handler1」,「Handler2」等將擴展「Handler」。
然後,處理程序將有一個「getSuccessor」方法,它將返回null(如果它是鏈的最後一個)或鏈的下一個Handler。
然後,一個「handleRequest(Request)」函數可以處理Request,或者將它傳遞給下一個鏈,如果以前的解決方案都沒有工作,它將返回null或拋出異常。
要添加一個新的處理程序到鏈中,編碼器將轉到鏈的最後一個元素,並告訴它有一個新的元素。要做一些事情,他只需要在鏈的第一個元素上調用handleRequest。

爲了解決這個問題,我建議使用不同的方法。
我也有一個基礎的「Handler」類,與「Handler1」,「Handler2」一樣,就像前面提到的方法一樣。
但是,不會有「getSuccessor」方法。相反,我會有一個帶有處理程序列表的Collection類(一個Vector,一個ArrayList,或者在這種情況下最好的)。
handleRequest函數仍然存在,但它不會將調用傳播到下一個處理程序。它只會處理請求或返回null。
要處理一個請求,一個會用

for(Handler handle : handlers){ 
    result = handle.handleRequest(request); 
    if(result!=null) return result; 
} 
throw new CouldNotParseRequestException(); //just like in the other approach 

或者,以防止代碼重複,一「的parseRequest(請求)」方法​​可以加入到集合類。 要添加一個新的處理程序,可以使用集合構造函數(或靜態{}塊或其他等價物),只需添加代碼「addHandler(new Handler3());」。

確切地說,我錯過了這種方法的責任鏈究竟有哪些優勢?哪種方法最好(假設有最好的方法)?爲什麼?每種設計方法會產生哪些潛在的錯誤和問題?

對於那些誰需要語境,這裏看起來像原來的代碼是什麼:

if(x instanceof Type1) 
{ 
//doSomething1 
} else if(x instanceof Type2) 
{ 
//doSomething2 
} 
//etc. 

回答

6

我比收藏家更喜歡收藏的想法。它使得處理這組處理程序變得簡單明瞭:集合接口是衆所周知的,每個人都知道如何遍歷List或不是什麼。

如果您使用朋友建議的後繼方式,請注意不要陷入非常深的遞歸(除非您的平臺支持尾部調用,我不知道JVM是否有這種能力)。

我不會建議在集合中添加任何方法。你會得到更復雜的設計,難以理解和難以修改。有兩個獨立的問題:存儲一組處理程序和解釋這個處理程序作爲一個責任鏈。通過迭代集合來處理請求的方法比集合管家方法的抽象層次更高,因此不應該屬於集合接口。

+0

非常深的遞歸不應該是一個問題。它相對較小(遠不止堆棧溢出系統所需的數千個方法調用)。 – luiscubal 2009-06-28 18:22:39

0

不能告訴我們,如果責任鏈就是你的答案,或者即使GoF的應用。訪客可能是正確的。沒有足夠的信息來確定。

這可能是你的問題可以用良好的老式多態處理。或者也許是一個使用鍵來挑選合適的處理程序對象的Map。

記住'做最簡單的事情'。除非你證明你自己需要它,否則不要跳樓。

我最近讀了關於促進這個想法的Anti-IF campaign。聽起來很貼切。

4

哪種方法最好取決於你的處理程序想要做什麼。

如果處理程序可以完全自行處理請求請求,那麼您的方法沒問題。處理程序沒有對其他處理程序的引用,這使處理程序接口變得簡單。與負責人鏈的標準實施不同,您可以從鏈的中間添加或刪除處理程序。實際上,您可以根據請求的類型選擇構建不同的鏈。

您的方法的一個問題是處理程序無法對請求執行預處理或後處理。如果這個功能是必需的,那麼責任鏈更好。在CoR中,處理程序是負責委託給鏈上下一個處理程序的處理程序,因此處理程序可以執行預處理和/或後處理,包括修改或替換鏈上下一個處理程序的響應。通過這種方式,CoR與Decorator非常相似;這只是意圖不同而已。

由於在CoR中,處理程序保留對鏈上下一個項目的引用,因此無法從鏈中間添加或刪除項目。 CoR的一個變體允許您添加或刪除鏈條中間的物品是一個過濾器鏈(請參閱,例如,javax.servlet.FilterChain)。

您展示的代碼示例是一堆基於對象類型做了不同行爲的「if」語句。如果這是您正在清理的代碼的典型代碼,那麼您可以簡單地將請求類型映射到所需的處理程序。

去除「if」語句的另一種方法是繼承。如果您需要執行某些操作,並且Web服務器有一個變體,而SOAP服務器有其他變體,則可以使用WebServerRequestHandler和SoapServerRequestHandler,每個擴展RequestHandler。繼承的優點是有一個更清晰的地方可以放置兩種類型的請求所共有的邏輯。缺點是由於Java沒有多重繼承,所以只能建模一維問題。