1. 簡介
本文將探討一種廣泛使用的行為設計模式:責任鏈模式。
我們可以在上一篇文章中找到更多設計模式。
2. 職責鏈模式
維基百科 將職責鏈模式定義為“包含命令對象源以及一系列處理對象”的設計模式。
鏈中的每個處理對象負責特定類型的命令,處理完成後,會將命令轉發到鏈中的下一個處理對象。
職責鏈模式適用於:
- 解耦命令發送者和接收者
- 在處理時選擇處理策略
那麼,讓我們來看一個簡單的示例。
3. 示例
我們將使用責任鏈模式來創建一個用於處理身份驗證請求的鏈條。
因此,輸入身份驗證提供者將是 命令,而每個身份驗證處理程序將是一個單獨的 處理器對象。
我們首先創建一個用於處理程序的基本抽象類:
public abstract class AuthenticationProcessor {
public AuthenticationProcessor nextProcessor;
// standard constructors
public abstract boolean isAuthorized(AuthenticationProvider authProvider);
}接下來,讓我們創建具體的處理器,這些處理器將擴展 AuthenticationProcessor:
public class OAuthProcessor extends AuthenticationProcessor {
public OAuthProcessor(AuthenticationProcessor nextProcessor) {
super(nextProcessor);
}
@Override
public boolean isAuthorized(AuthenticationProvider authProvider) {
if (authProvider instanceof OAuthTokenProvider) {
return true;
} else if (nextProcessor != null) {
return nextProcessor.isAuthorized(authProvider);
}
return false;
}
}public class UsernamePasswordProcessor extends AuthenticationProcessor {
public UsernamePasswordProcessor(AuthenticationProcessor nextProcessor) {
super(nextProcessor);
}
@Override
public boolean isAuthorized(AuthenticationProvider authProvider) {
if (authProvider instanceof UsernamePasswordProvider) {
return true;
} else if (nextProcessor != null) {
return nextProcessor.isAuthorized(authProvider);
}
return false;
}
}以下是翻譯後的內容:
在這裏,我們創建了兩個用於處理傳入授權請求的具體處理器:UsernamePasswordProcessor 和 OAuthProcessor
對於每個處理器,我們都覆蓋了 isAuthorized 方法。
現在,我們來創建一些測試用例:
public class ChainOfResponsibilityTest {
private static AuthenticationProcessor getChainOfAuthProcessor() {
AuthenticationProcessor oAuthProcessor = new OAuthProcessor(null);
return new UsernamePasswordProcessor(oAuthProcessor);
}
@Test
public void givenOAuthProvider_whenCheckingAuthorized_thenSuccess() {
AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor();
assertTrue(authProcessorChain.isAuthorized(new OAuthTokenProvider()));
}
@Test
public void givenSamlProvider_whenCheckingAuthorized_thenSuccess() {
AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor();
assertFalse(authProcessorChain.isAuthorized(new SamlTokenProvider()));
}
}上面的示例創建了一個身份驗證處理鏈:UsernamePasswordProcessor -> OAuthProcessor。 在第一個測試中,授權成功,而在其他測試中,授權失敗。
首先,UsernamePasswordProcessor 檢查身份提供者是否為 UsernamePasswordProvider 實例。
由於這不是預期的輸入,UsernamePasswordProcessor 將委託給 OAuthProcessor。
最後,OAuthProcessor 處理命令。 在第一個測試中,存在匹配項並且測試通過。 在第二個測試中,鏈中的處理器數量不足,因此測試失敗。
4. 實現原則
在實現鏈式責任模式時,我們需要牢記以下重要原則:
- 鏈中的每個處理器都將擁有處理命令的實現
- 在上面的示例中,所有處理器都擁有 isAuthorized 的實現
- 鏈中的每個處理器都應該擁有指向下一個處理器的引用
- 上面,UsernamePasswordProcessor 委託給 OAuthProcessor
- 每個處理器負責將命令委託給下一個處理器,因此要小心命令丟失的情況
- 再次在上面的示例中,如果命令是 SamlProvider 的實例,則請求可能不會被處理,並且會被拒絕授權
- 處理器不應形成遞歸循環
- 在上面的示例中,我們的鏈中沒有循環:UsernamePasswordProcessor -> OAuthProcessor. 但是,如果我們明確地將 UsernamePasswordProcessor 設置為 OAuthProcessor 的下一個處理器,那麼我們就會在鏈中形成一個循環:UsernamePasswordProcessor -> OAuthProcessor -> UsernamePasswordProcessor. 通過在構造函數中獲取下一個處理器可以幫助解決這個問題
- 鏈中的只有一個處理器處理給定的命令
- 在上面的示例中,如果傳入的命令包含 OAuthTokenProvider 的實例,則只有 OAuthProcessor 會處理該命令
5. 實際應用
在Java世界中,我們每天都受益於責任鏈模式。其中一個經典的例子是Java中的Servlet過濾器,它們允許多個過濾器處理HTTP請求。儘管在這種情況中,每個過濾器調用鏈,而不是下一個過濾器。
以下代碼片段可以幫助更好地理解Servlet過濾器中該模式:
public class CustomFilter implements Filter {
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
// process the request
// pass the request (i.e. the command) along the filter chain
chain.doFilter(request, response);
}
}如代碼片段所示,我們需要調用 FilterChain 的 doFilter 方法,以便將請求傳遞給鏈中的下一個處理器。
6. 缺點
現在我們已經瞭解了責任鏈模式的有趣之處,接下來讓我們考慮一些缺點:
- 通常,它容易中斷:
- 如果一個處理器未能調用下一個處理器,則命令將被丟棄
- 如果一個處理器調用了錯誤的處理器,可能會導致循環
- 它可能會創建深層堆棧跟蹤,從而影響性能
- 它可能導致處理器之間出現重複代碼,從而增加維護成本
7. 結論
在本文中,我們通過使用一個鏈條來授權傳入的身份驗證請求,探討了職責鏈模式及其優勢和劣勢。