說(shuo)說(shuo)設計(ji)模(mo)式(shi)~責(ze)任鏈模(mo)式(shi)
責任鏈模式
它是一種設計模塊,主要將操作流程與具體操作解耦,讓每個操作都可以設置自己的操作流程,這對于工作流應用是一個不錯的選擇!
下(xia)(xia)面是官方(fang)標準的(de)(de)(de)定義:責(ze)(ze)任(ren)鏈模式是一種設計(ji)模式。在責(ze)(ze)任(ren)鏈模式里,很多對(dui)(dui)象由(you)每一個(ge)對(dui)(dui)象對(dui)(dui)其下(xia)(xia)家的(de)(de)(de)引用而連接(jie)起來形成(cheng)一條鏈。請(qing)求(qiu)(qiu)(qiu)在這(zhe)(zhe)(zhe)個(ge)鏈上傳遞,直到(dao)鏈上的(de)(de)(de)某一個(ge)對(dui)(dui)象決定處理此請(qing)求(qiu)(qiu)(qiu)。發(fa)出這(zhe)(zhe)(zhe)個(ge)請(qing)求(qiu)(qiu)(qiu)的(de)(de)(de)客戶(hu)端(duan)并不(bu)知道鏈上的(de)(de)(de)哪(na)一個(ge)對(dui)(dui)象最終處理這(zhe)(zhe)(zhe)個(ge)請(qing)求(qiu)(qiu)(qiu),這(zhe)(zhe)(zhe)使得系統可以在不(bu)影響客戶(hu)端(duan)的(de)(de)(de)情(qing)況下(xia)(xia)動(dong)態地(di)重(zhong)新組織和分配責(ze)(ze)任(ren)。
使用場景
責任鏈模(mo)式(shi)在以(yi)下(xia)(xia)情(qing)況下(xia)(xia)可以(yi)被用到:
-
多個(ge)對象(xiang)(xiang)按(an)照特定順序依(yi)次(ci)處理請(qing)求(qiu):當存在多個(ge)對象(xiang)(xiang)需要依(yi)次(ci)處理請(qing)求(qiu),并且每個(ge)對象(xiang)(xiang)都有可(ke)能處理請(qing)求(qiu)或將請(qing)求(qiu)傳遞給(gei)下一個(ge)對象(xiang)(xiang)時(shi),責(ze)任鏈模(mo)式可(ke)以很好(hao)地組織和管(guan)理這(zhe)些對象(xiang)(xiang)。
-
需(xu)要動態(tai)指定(ding)處理對象:責(ze)任鏈模式可以通(tong)過動態(tai)設置責(ze)任鏈的關系(xi),靈活地指定(ding)處理對象。可以根(gen)據實際情況(kuang)動態(tai)增加(jia)、移(yi)除或調整(zheng)處理者的順序,而(er)不(bu)需(xu)要修改客(ke)戶端代碼。
-
避免請求發送者(zhe)(zhe)和接收者(zhe)(zhe)之間的(de)(de)耦(ou)合關(guan)系(xi):使用責任鏈模式可(ke)以將(jiang)請求發送者(zhe)(zhe)和接收者(zhe)(zhe)解(jie)耦(ou),發送者(zhe)(zhe)只需要將(jiang)請求發送給責任鏈的(de)(de)第一個處理(li)者(zhe)(zhe),而不需要關(guan)心具(ju)體(ti)是(shi)哪個處理(li)者(zhe)(zhe)來處理(li)請求。
-
處(chu)理請求(qiu)的(de)對象(xiang)需要進行動(dong)態(tai)配(pei)置(zhi)(zhi):責(ze)任鏈模式可以通過配(pei)置(zhi)(zhi)文件(jian)、數(shu)據庫等方式來(lai)動(dong)態(tai)配(pei)置(zhi)(zhi)處(chu)理者的(de)順序和條件(jian),而不需要修改代碼。
-
請求的(de)(de)處(chu)(chu)理(li)(li)邏(luo)(luo)輯具有變(bian)化(hua)和(he)擴展(zhan)(zhan)的(de)(de)可(ke)能性:責(ze)任鏈模(mo)式(shi)可(ke)以很方(fang)便地對處(chu)(chu)理(li)(li)邏(luo)(luo)輯進(jin)行(xing)擴展(zhan)(zhan)和(he)變(bian)化(hua)。可(ke)以通過增加新(xin)的(de)(de)處(chu)(chu)理(li)(li)者來(lai)擴展(zhan)(zhan)處(chu)(chu)理(li)(li)邏(luo)(luo)輯,也(ye)可(ke)以通過修改(gai)現有處(chu)(chu)理(li)(li)者的(de)(de)條件判斷(duan)來(lai)變(bian)化(hua)處(chu)(chu)理(li)(li)邏(luo)(luo)輯。
一(yi)些常見(jian)的(de)使用場景包括(kuo):
- 客戶端請求的處理:比如Web請求處理、日志記錄、安全認證等。
- 錯誤處理和異常處理:將異常或錯誤從一個處理者傳遞給下一個處理者進行處理,直到找到能夠處理它的處理者。
- 負載均衡:按照一定策略將請求傳遞給多個服務器進行處理,直到有服務器能夠處理請求。
- 事件驅動系統:通過觸發事件并將事件傳遞給相關的處理者來實現事件的處理和響應。
需要注意(yi)的是(shi),責任鏈(lian)模式并(bing)不保證一(yi)定會有處理(li)(li)者(zhe)能夠處理(li)(li)請求,因此需要在責任鏈(lian)末端進行(xing)處理(li)(li)請求無法被處理(li)(li)的情況。
實例中算法說明
- 每種handler只使用一次
- 按最高優先級去使用,符合就用,不符合就走下一個策略
- 具體鏈條
- VipHandler >10000
- BigGiftHandler >1000
- DiscountHandler >200
- CouponHandler >100
- DiscountHandler >200
- BigGiftHandler >1000
- VipHandler >10000
代碼實現
- 定義處理請求的接口(Handler):
public interface Handler {
void handleRequest(Order order);
void setNextHandler(Handler nextHandler);
}
- 具體處理請求CouponHandler
public class CouponHandler implements Handler {
private static final Logger logger = LoggerFactory.getLogger(CouponHandler.class);
private Handler nextHandler;
@Override
public void handleRequest(Order order) {
// 這是可以想用多個責任鏈的實現,如果是只使用1個,需要加上200的限制,就是每個訂單只能現用1種優惠
if (order.getTotalPrice() >= 100) {
// 應用優惠券打9折
order.setTotalPrice(order.getTotalPrice() * .9);
logger.info("Coupon 0.9 applied to order,{}", order.getOrderId());
}else if (nextHandler != null) {
nextHandler.handleRequest(order);
}
}
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
}
- 具體處理請求DiscountHandler
public class DiscountHandler implements Handler {
private static final Logger logger = LoggerFactory.getLogger(CouponHandler.class);
private static final double DISCOUNT = .8;
private Handler nextHandler;
@Override
public void handleRequest(Order order) {
if (order.getTotalPrice() >= 200) {
// 應用折扣,折扣為標準的8折
order.setTotalPrice(order.getTotalPrice() * DISCOUNT);
logger.info("Discount 0.8 applied to order,{}", order.getOrderId());
}
else if (nextHandler != null) {
nextHandler.handleRequest(order);
}
}
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
}
這個具體命令是可以擴(kuo)展的(de),這也是職責(ze)鏈(責(ze)任鏈)的(de)最重要的(de)體現。
- 命令模式,方便將命令之久,及后期的動態配置
@Data
@AllArgsConstructor
public class HandlerModel implements Comparable<HandlerModel> {
private String title;
private String classPath;
private Integer sort;
@Override
public int compareTo(HandlerModel o) {
return o.getSort() - this.sort; // 降序
}
}
- 命令工廠,這里使用了強編碼的方式配置命令,真實項目中,這些命令可以配置到數據庫中
/**
* 折扣工廠.
*
* @author lind
* @date 2023/6/28 9:22
* @since 1.0.0
*/
public class HandlerFactory {
public static Handler handlerFactory() {
List<HandlerModel> handlerModels = new ArrayList<>();
handlerModels.add(
new HandlerModel("CouponHandler", "com.lind.common.pattern.chinaorder.handler.CouponHandler", 1));
handlerModels.add(
new HandlerModel("DiscountHandler", "com.lind.common.pattern.chinaorder.handler.DiscountHandler", 2));
handlerModels.add(
new HandlerModel("BigGiftHandler", "com.lind.common.pattern.chinaorder.handler.BigGiftHandler", 3));
handlerModels.add(
new HandlerModel("VipHandler", "com.lind.common.pattern.chinaorder.handler.VipHandler", 4));
return createHandler(handlerModels);
}
private static Handler createHandler(List<HandlerModel> handlerModels) {
Handler handler = null;
Handler previousHandler = null;
for (HandlerModel handlerModel : handlerModels.stream().sorted().collect(Collectors.toList())) {
try {
Handler currentHandler = (Handler) Class.forName(handlerModel.getClassPath()).newInstance();
if (previousHandler != null) {
previousHandler.setNextHandler(currentHandler);
}
else {
handler = currentHandler;
}
previousHandler = currentHandler;
}
catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
return handler;
}
}
- 看一下測試的代碼
public static void main(String[] args) {
Handler couponHandler = HandlerFactory.handlerFactory();
Order order1 = new Order("OR01", 150, true);
couponHandler.handleRequest(order1);
logger.info("order1:{}\n", order1.getTotalPrice());
Order order2 = new Order("OR02", 250, true);
couponHandler.handleRequest(order2);
logger.info("order2:{}\n", order2.getTotalPrice());
Order order3 = new Order("OR03", 50, true);
couponHandler.handleRequest(order3);
logger.info("order3:{}\n", order3.getTotalPrice());
Order order4 = new Order("OR04", 5001, true);
couponHandler.handleRequest(order4);
logger.info("order4:{}\n", order4.getTotalPrice());
Order order5 = new Order("OR05", 10001, true);
couponHandler.handleRequest(order5);
logger.info("order5:{}\n", order5.getTotalPrice());

待續……
回到目錄