資安

從風控鏈理解責任鏈模式

前言

  責任鏈是一種行為型模式。顧名思義,由多個有不同處理能力節點組成的一條執行鏈。當一個事件進來時,會判斷當前節點是否有處理的能力,反之轉入下一個節點進行處理。可以從支付的風控鏈這個場景,深入的理解責任鏈模式。

定義

  責任鏈模式:包含了一些命令和一系列的處理對象。每一個處理對象決定了它能處理哪些命令對象,它也知道如何將它不能處理的命令對象傳遞給該鏈中的下一個處理對象。

如何理解

  信用卡套現,花唄套現,白條套現,類似的名詞對於我們來講應該不陌生吧。在支付系統中會有一套風控系統,來避免發生類似的事情出現。而風控系統就是責任鏈模式的一種應用。

比如說給xxx商戶配置的風控規則是:

TOP 信用卡 花唄 白條
木屋燒烤 500/日 300/日 300/日

  當用戶向商家付款時,會根據不同的支付方式,來判斷是否觸發風控條件。如果達到觸發條件則不允許使用該方式支付。來避免發生信用卡套現類似的事情。

我們寫個簡單的風控程序來理解一下:

UML

代碼示例

RiskHandler

public abstract class RiskHandler {

    /**
     * 支付方式
     */
    private String payType;

    private RiskHandler nextHandler;


    public RiskHandler(String payType) {
        this.payType = payType;
    }

    /**
     * 是否能支付
     * @param order 訂單
     */
    protected abstract boolean canPay(Order order);


    /**
     * 處理器
     * @param order 訂單
     * @return 返回true 或者 false
     */
    public final boolean handler(Order order){
        if(order.getPayType().equals(this.payType)){
            return this.canPay(order);
        }else {
            if (this.nextHandler != null){
                return this.nextHandler.handler(order);
            }else{
                throw new IllegalArgumentException("支付方式有誤");
            }
        }
    }

    public void setNextHandler(RiskHandler handler){
        this.nextHandler = handler;
    }
}

CreditHandler

public class CreditHandler extends RiskHandler {

    /**
     * 500 限額
     */
    private BigDecimal limitAmount = BigDecimal.valueOf(500);


    public CreditHandler() {
        super(PayTypeEnum.CREDIT.getPayType());
    }

    @Override
    protected boolean canPay(Order order) {
        if(order.getAmount().compareTo(limitAmount) < 0){
            limitAmount = limitAmount.subtract(order.getAmount());
            return true;
        }else {
            return false;
        }
    }
}

HuabeiHandler

public class HuabeiHandler extends RiskHandler {

    /**
     * 300 限額
     */
    private BigDecimal limitAmount = BigDecimal.valueOf(300);


    public HuabeiHandler() {
        super(PayTypeEnum.HUA_BEI.getPayType());
    }

    @Override
    protected boolean canPay(Order order) {
        if(order.getAmount().compareTo(limitAmount) < 0){
            limitAmount = limitAmount.subtract(order.getAmount());
            return true;
        }else {
            return false;
        }
    }
}

BaiTiaoHandler

public class BaiTiaoHandler extends RiskHandler {

    /**
     * 300 限額
     */
    private BigDecimal limitAmount = BigDecimal.valueOf(300);

    public BaiTiaoHandler() {
        super(PayTypeEnum.BAI_TIAO.getPayType());
    }

    @Override
    protected boolean canPay(Order order) {
        if(order.getAmount().compareTo(limitAmount) < 0){
            limitAmount = limitAmount.subtract(order.getAmount());
            return true;
        }else {
            return false;
        }
    }
}

Order

public interface Order {

    /**
     * 獲取支付方式
     * @return 支付方式
     */
    String getPayType();

    /**
     * 獲取訂單金額
     * @return 訂單金額
     */
    BigDecimal getAmount();

}

ConsumeOrder

public class ConsumeOrder implements Order {

    private String payType;

    private BigDecimal amount;

    public ConsumeOrder(String payType, BigDecimal amount) {
        this.payType = payType;
        this.amount = amount;
    }

    @Override
    public String getPayType() {
        return this.payType;
    }

    @Override
    public BigDecimal getAmount() {
        return this.amount;
    }
}

PayTypeEnum

public enum  PayTypeEnum {

    /**
     * 花唄
     */
    HUA_BEI("hua_bei"),

    /**
     * 白條
     */
    BAI_TIAO("bai_tiao"),

    /**
     * 信用卡
     */
    CREDIT("credit"),

    ;

    private String payType;


    PayTypeEnum(String payType) {
        this.payType = payType;
    }

    public String getPayType() {
        return payType;
    }
}

PayRiskControlService

public class PayRiskControlService {

    public boolean canPay(Order order){
        
        // 設置風控
        RiskHandler creditHandler = new CreditHandler();
        
        RiskHandler huabeiHandler = new HuabeiHandler();
        
        RiskHandler baiTiaoHandler = new BaiTiaoHandler();

        creditHandler.setNextHandler(huabeiHandler);

        huabeiHandler.setNextHandler(baiTiaoHandler);

        return creditHandler.handler(order);
    }
}

Test

public class Test {

    public static void main(String[] args) {

        // 花唄訂單
        ConsumeOrder huabeiOrder = new ConsumeOrder(
                PayTypeEnum.HUA_BEI.getPayType(), BigDecimal.valueOf(200)
        );

        // 白條訂單
        ConsumeOrder baitiaoOrder = new ConsumeOrder(
                PayTypeEnum.BAI_TIAO.getPayType(), BigDecimal.valueOf(301)
        );

        // 加載風控系統
        PayRiskControlService riskControlService = new PayRiskControlService();

        // 創建訂單
        boolean canPayOfHuabei = riskControlService.canPay(huabeiOrder);
        boolean canPayOfBaitiao = riskControlService.canPay(baitiaoOrder);

        System.out.println("canPayOfHuabei = " + canPayOfHuabei);
        System.out.println("canPayOfBaitiao = " + canPayOfBaitiao);
    }
}

測試結果

ConsumeOrder{payType='hua_bei', amount=200} canPay: true

ConsumeOrder{payType='bai_tiao', amount=301} canPay: false

優點

  • 降低耦合度,將請求和處理分開。
  • 簡化了對象,是對象不需要知道鏈的結構。
  • 增強給對象指派職責的靈活性,通過改變鏈內的成員或者調動他們的次序,運行動態的新增或者刪除責任。
  • 增加新的處理類很方便。

缺點

  • 不能保證請求一定被接收
  • 系統性能將受到一定影響,而且在調試代碼時不太方便,可能會造成循環調用。
  • 可能不容易觀察運行時的特徵,有礙於除錯。

應用場景

  • 有多個對象可以處理同一個請求,具體是哪個對象處理由該請求運行時刻自動確定。
  • 在不明確接受者的情況下,向多個對象中的一個提交一個請求。
  • 可以動態指定一組對象處理請求。

責任鏈模式的兩種情況

純的職責鏈模式

  一個請求必須被某一個處理者對象所接收,且一個具體處理者對某個請求的處理只能採用以下兩種行為之一:自己處理(承擔責任),把責任推給下家處理。

不純的職責鏈模式

  允許出現某一個具體處理者對象在承擔了請求的一部分責任後又將剩餘的責任傳給下家的情況,且一個請求可以最終不被任何接收端對象所接收。比如說,多級審核登錄安全監測

注意的點

  鏈中節點數量需要控制,避免出現超長鏈的情況,一般的做法是在Handler中設置一個最大節點數量,在setNext方法中判斷是否已經是超過其閾值,超過則不允許該鏈建立,避免無意識地破壞系統性能。

參考文章

結尾

  如果覺得對你有幫助,可以多多評論,多多點贊哦,也可以到我的主頁看看,說不定有你喜歡的文章,也可以隨手點個關注哦,謝謝。

Leave a Reply

Your email address will not be published. Required fields are marked *