設(shè)計模式之不一樣的責任鏈模式
責任鏈模式(Chain of Responsibility Pattern)是一種行為型設(shè)計模式,它通過將請求的發(fā)送者和接收者解耦,使多個對象都有機會處理請求。在這個模式中,請求沿著一個處理鏈依次傳遞,直到有一個對象能夠處理它為止。
本文將詳細介紹責任鏈模式的概述、應用場景以及代碼示例,來幫助讀者更好地理解和應用這個模式。
1. 簡介
模式概述
責任鏈模式的核心思想是將請求的發(fā)送者和接收者解耦,使得多個對象都有機會處理請求。在責任鏈模式中,請求會沿著一個處理鏈依次傳遞,每個處理者都有機會處理請求,如果一個處理者不能處理請求,則將請求傳遞給下一個處理者,直到有一個處理者能夠處理它。
責任鏈模式包含以下幾個角色:
責任鏈模式類結(jié)構(gòu)
- 抽象處理者(Handler):定義了處理請求的接口,通常包含一個指向下一個處理者的引用,用于將請求傳遞給下一個處理者。
- 具體處理者(ConcreteHandler):實現(xiàn)了處理請求的接口,具體處理者可以決定是否處理請求,如果不能處理,則將請求傳遞給下一個處理者。
- 客戶端(Client):創(chuàng)建處理者對象并組成責任鏈的結(jié)構(gòu),負責將請求發(fā)送給第一個處理者。
優(yōu)點與缺點
優(yōu)點:
- 責任鏈模式可以實現(xiàn)請求的發(fā)送者和接收者之間的解耦。發(fā)送者只需要將請求發(fā)送給第一個處理者,無需關(guān)心具體是哪個處理者來處理。這樣,系統(tǒng)的靈活性大大增強,可以隨時增加或修改處理者的順序。
- 責任鏈模式能夠避免請求的發(fā)送者和接收者之間的緊耦合。每個處理者只需要關(guān)心自己負責的請求類型,無需關(guān)心其他請求。這樣,系統(tǒng)的可維護性也得到了提升。
- 責任鏈模式可以靈活地動態(tài)添加或刪除處理者。我們可以根據(jù)實際情況來調(diào)整責任鏈的結(jié)構(gòu),以滿足不同的業(yè)務需求。
缺點:
- 復雜度會明顯提升,如果責任鏈過長或者處理者之間的關(guān)系復雜,可能還會導致性能下降和調(diào)試困難。
應用場景
責任鏈模式在許多不同的應用場景中都有廣泛的應用。下面列舉了一些常見的應用場景:
- 請求處理鏈:當一個請求需要經(jīng)過多個處理步驟或處理者進行處理時,可以使用責任鏈模式。每個處理者負責一部分邏輯,處理完后可以選擇將請求傳遞給下一個處理者,從而形成一個處理鏈。
- 日志記錄:在日志系統(tǒng)中,可以使用責任鏈模式來記錄日志。不同的處理者可以負責不同級別的日志記錄,例如,一個處理者負責記錄錯誤日志,另一個處理者負責記錄調(diào)試日志,然后按照鏈式結(jié)構(gòu)傳遞日志。
- 身份驗證和權(quán)限檢查:在身份驗證和權(quán)限檢查系統(tǒng)中,可以使用責任鏈模式來驗證用戶的身份和權(quán)限。每個處理者可以檢查特定的條件,例如用戶名和密碼的正確性、賬戶是否鎖定等。如果一個處理者無法通過驗證,可以將請求傳遞給下一個處理者。
- 數(shù)據(jù)過濾和轉(zhuǎn)換:在數(shù)據(jù)處理過程中,可以使用責任鏈模式來進行數(shù)據(jù)過濾和轉(zhuǎn)換。每個處理者可以根據(jù)特定的條件過濾數(shù)據(jù)或?qū)?shù)據(jù)進行轉(zhuǎn)換,然后將處理后的數(shù)據(jù)傳遞給下一個處理者。
- 錯誤處理和異常處理:在錯誤處理和異常處理系統(tǒng)中,可以使用責任鏈模式來處理錯誤和異常。不同的處理者可以處理不同類型的錯誤或異常,并根據(jù)需要將錯誤或異常傳遞給下一個處理者進行進一步處理或記錄。
2. Java 代碼示例
在 Java 中實現(xiàn)責任鏈模式有多種方式,包括基于接口、基于抽象類、基于注解等。下面將詳細介紹基于接口的常見實現(xiàn)方式。
基于接口的實現(xiàn)方式是通過定義一個處理請求的接口,每個處理者實現(xiàn)這個接口,并在自己的實現(xiàn)中決定是否處理請求和傳遞請求給下一個處理者。
首先,我們定義一個處理請求的接口 Handler 以及請求入?yún)?nbsp;Request:
public interface Handler {
void handleRequest(Request request);
}
public class Request {
private String type;
// 省略getter、setter
}
然后,我們創(chuàng)建3個具體的處理者類實現(xiàn)這個接口,在具體處理者類的實現(xiàn)中,首先判斷自己是否能夠處理請求,如果能夠處理,則進行處理;否則將請求傳遞給下一個處理者。代碼如下:
public class ConcreteHandlerA implements Handler {
private Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public void handleRequest(Request request) {
if (request.getType().equals("A")) {
// 處理請求的邏輯
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
public class ConcreteHandlerB implements Handler {
private Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public void handleRequest(Request request) {
if (request.getType().equals("B")) {
// 處理請求的邏輯
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
public class ConcreteHandlerC implements Handler {
private Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public void handleRequest(Request request) {
if (request.getType().equals("C")) {
// 處理請求的邏輯
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
接下來,我們創(chuàng)建一個客戶端類 Client,用于創(chuàng)建處理者對象并組成責任鏈的結(jié)構(gòu):
public class Client {
public static void main(String[] args) {
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
Handler handlerC = new ConcreteHandlerC();
handlerA.setSuccessor(handlerB);
handlerB.setSuccessor(handlerC);
// 創(chuàng)建請求并發(fā)送給第一個處理者
Request request = new Request("A");
handlerA.handleRequest(request);
}
}
在客戶端類中,我們創(chuàng)建了具體的處理者對象,并通過 setSuccessor() 方法將它們組成一個責任鏈的結(jié)構(gòu)。然后,創(chuàng)建一個請求對象,并將請求發(fā)送給第一個處理者。
基于接口的實現(xiàn)方式簡單直觀,每個處理者只需要實現(xiàn)一個接口即可。但是它的缺點是如果責任鏈較長,需要創(chuàng)建多個處理者對象,增加了系統(tǒng)的復雜性和資源消耗。下面基于 Spring 框架實現(xiàn)一個高級版的責任鏈模式。
3. Spring 代碼示例
在實際開發(fā)中,一個請求會在多個處理器之間流轉(zhuǎn),每個處理器都可以處理請求。
假設(shè)我們有一個 Spring 框架開發(fā)的訂單處理系統(tǒng),訂單需要依次經(jīng)過訂單檢查、庫存處理、支付處理。如果某個處理環(huán)節(jié)無法處理訂單,將會終止處理并返回錯誤信息,只有每個處理器都完成了請求處理,這個訂單才算法下單成功。
首先,我們定義一個訂單類 Order:
@Data
@AllArgsConstructor
public class orderNo {
private String orderNumber;
private String paymentMethod;
private boolean stockAvailability;
private String shippingAddress;
}
然后,我們定義一個抽象訂單處理者類 OrderHandler:
public abstract class OrderHandler {
public abstract void handleOrder(Order order);
}
接下來,我們創(chuàng)建具體的訂單處理者類繼承自抽象訂單處理者類,實現(xiàn)相應的方法,并注冊到 Spring 中,
@Component
public class CheckOrderHandler extends OrderHandler {
public void handleOrder(Order order) {
if (StringUtils.isBlank(order.getOrderNo())) {
throw new RuntimeException("訂單編號不能為空");
}
if (order.getPrice().compareTo(BigDecimal.ONE) <= 0) {
throw new RuntimeException("訂單金額不能小于等于0");
}
if (StringUtils.isBlank(order.getShippingAddress())) {
throw new RuntimeException("收貨地址不能為空");
}
System.out.println("訂單參數(shù)檢驗通過");
}
}
@Component
public class StockHandler extends OrderHandler {
public void handleOrder(Order order) {
if (!order.isStockAvailability()) {
throw new RuntimeException("訂單庫存不足");
}
System.out.println("庫存扣減成功");
}
}
@Component
public class AliPaymentHandler extends OrderHandler {
public void handleOrder(Order order) {
if (!order.getPaymentMethod().equals("支付寶")) {
throw new RuntimeException("不支持支付寶以外的支付方式");
}
System.out.println("支付寶預下單成功");
}
}
在具體訂單處理者類的實現(xiàn)中,CheckOrderHandler 負責做訂單參數(shù)檢查、StockHandler 負責做庫存扣減、AliPaymentHandler 負責做預下單,每個處理者的邏輯都是相互獨立各不不干擾。
最后,我們創(chuàng)建一個訂單生產(chǎn)鏈條 BuildOrderChain ,用于組成責任鏈的鏈條處理結(jié)構(gòu):
@Component
public class BuildOrderChain {
@Autowired
private AliPaymentHandler aliPaymentHandler;
@Autowired
private CheckOrderHandler checkOrderHandler;
@Autowired
private StockHandler stockHandler;
List<OrderHandler> list = new ArrayList<>();
@PostConstruct
public void init() {
// 1. 檢查訂單參數(shù)
list.add(checkOrderHandler);
// 2. 扣減庫存
list.add(stockHandler);
// 3. 支付寶預下單
list.add(aliPaymentHandler);
}
public void doFilter(Order order) {
for (OrderHandler orderHandler : this.list) {
orderHandler.handleOrder(order);
}
}
}
訂單生產(chǎn)鏈條 BuildOrderChain 類中,我們通過 @PostConstruct 注解下的 init() 初始化方法,將具體的訂單處理者按代碼順序組成一個責任鏈的結(jié)構(gòu)。然后通過 doFilter(order) 方法遍歷處理者集合依次處理。
運行代碼,
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class OrderChainTest {
@Autowired
private BuildOrderChain buildOrderChain;
@Test
public void test() {
Order order = new Order("123456", "支付寶",
true, "長沙", new BigDecimal("100"));
buildOrderChain.doFilter(order);
}
}
-------------------------------
訂單參數(shù)檢驗通過
庫存扣減成功
支付寶預下單成功
可以看到訂單依次經(jīng)過校驗處理器、庫存處理器和支付處理器進行處理,直到最后完成整個訂單的處理。
在舉個例子,假如我們的訂單針對的是虛擬不限庫存商品,我們不需要進行庫存扣減,那我們可以直接新建 VirtualGoodsOrderChain 虛擬商品訂單生產(chǎn)鏈條類,代碼如下,
@Component
public class VirtualGoodsOrderChain {
@Autowired
private AliPaymentHandler aliPaymentHandler;
@Autowired
private CheckOrderHandler checkOrderHandler;
List<OrderHandler> list = new ArrayList<>();
@PostConstruct
public void init() {
// 1. 檢查訂單參數(shù)
list.add(checkOrderHandler);
// 2 支付寶預下單
list.add(aliPaymentHandler);
}
public void doFilter(Order order) {
for (OrderHandler orderHandler : this.list) {
orderHandler.handleOrder(order);
}
}
}
運行代碼:
@Test
public void virtualOrderTest() {
Order order = new Order("123456", "支付寶", true, "長沙", new BigDecimal("100"));
virtualGoodsOrderChain.doFilter(order);
}
-------------------------------------------
訂單參數(shù)檢驗通過
支付寶預下單成功
4. 總結(jié)
總的來說,責任鏈模式適用于存在多個處理步驟、每個處理步驟具有獨立邏輯或條件、需要靈活組合和擴展的場景。通過責任鏈模式,可以將復雜的處理邏輯拆分為多個獨立的處理步驟,并且可以動態(tài)地組合和調(diào)整處理步驟的順序,從而提高系統(tǒng)的靈活性和可維護性。希望本文能夠幫助讀者理解和應用責任鏈模式,提升軟件設(shè)計和開發(fā)的能力。