設計模式10-策略模式與責任鏈模式詳解


1.10.策略模式與責任鏈模式詳解

1.10.1.策略模式詳解

時長:1h15min

10.1.1.策略模式的定義

定義

  策略模式【Strategy Pattern】,又叫政策模式【Policy Pattern】,它是將定義的算法家族,分別封裝起來,讓它們之間可以相互替換,從而

讓算法的變化不會影響到使用算法的用戶。

  可以避免多重分支的if...else...和switch語句。

  屬於行為型模式。

 

10.1.1.1.策略模式在生活中應用場景

  階梯個稅【工資收入不同,個稅算法不同】

  移動支付方式選擇【微信,支付寶,銀聯】

  出行交通方式選擇【火車,飛機,汽車,輪船】

10.1.1.1、2.策略模式的應用場景

1.假如系統中有很多類,而他們的區別僅僅在於他們的行為 不同

2.一個系統需要動態地在幾種算法中選擇一種。

3.需要屏蔽算法規則。

10.1.2.策略模式的通用實現

10.1.2.1.類圖設計

 

 

 10.1.2.2.代碼實現
1.頂層策略接口
package com.wf.strategy.general;

/**
 * @ClassName IStrategy
 * @Description 頂層策略接口
 * @Author wf
 * @Date 2020/6/18 10:11
 * @Version 1.0
 */
public interface IStrategy {
    /**
     * 算法接口
     */
    void algorithm();
}

 

2.策略子類實現
package com.wf.strategy.general;

/**
 * @ClassName ConcreteStrategyA
 * @Description 具體策略子類A
 * @Author wf
 * @Date 2020/6/18 10:13
 * @Version 1.0
 */
public class ConcreteStrategyA implements IStrategy {
    @Override
    public void algorithm() {
        System.out.println("這是算法A");
    }
}
package com.wf.strategy.general;

/**
 * @ClassName ConcreteStrategyB
 * @Description 具體策略子類B
 * @Author wf
 * @Date 2020/6/18 10:13
 * @Version 1.0
 */
public class ConcreteStrategyB implements IStrategy {
    @Override
    public void algorithm() {
        System.out.println("這是算法B");
    }
}

 

3.上下文對象
package com.wf.strategy.general;

/**
 * @ClassName Context
 * @Description 上下文對象
 * @Author wf
 * @Date 2020/6/18 10:14
 * @Version 1.0
 */
public class Context {
    private IStrategy strategy;

    public Context(IStrategy strategy) {
        this.strategy = strategy;
    }
    //由構造器中傳參實現子類類型,來決定選擇哪一種算法
    public void algorithm(){
        this.strategy.algorithm();
    }
}

 

4.測試類
package com.wf.strategy.general;

/**
 * @ClassName Test
 * @Description 測試類
 * @Author wf
 * @Date 2020/6/18 10:15
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        //使用時,客戶選擇一種策略
        IStrategy strategy = new ConcreteStrategyA();
        //封裝到上下文中
        Context context = new Context(strategy);
        //調用策略方法
        context.algorithm();
    }
}

 

測試結果如下:

 

 

 

10.1.3.策略模式的實現示例之促銷案例

10.1.3.1.代碼實現
1.頂層接口
package com.wf.strategy.demo.promotion;

/**
 * @ClassName IPromotionStrategy
 * @Description 促銷策略接口
 * @Author wf
 * @Date 2020/6/18 10:31
 * @Version 1.0
 */
public interface IPromotionStrategy {
    /**
     * 促銷方法
     */
    void doPromotion();
}

 

2.實現子類
package com.wf.strategy.demo.promotion;

/**
 * @ClassName GroupPurchaseStrategy
 * @Description 團購促銷
 * @Author wf
 * @Date 2020/6/18 10:36
 * @Version 1.0
 */
public class GroupPurchaseStrategy implements IPromotionStrategy{
    @Override
    public void doPromotion() {
        System.out.println("5人成團,可以優惠");
    }
}
package com.wf.strategy.demo.promotion;

/**
 * @ClassName CashRollbackStrategy
 * @Description 返現促銷
 * @Author wf
 * @Date 2020/6/18 10:34
 * @Version 1.0
 */
public class CashRollbackStrategy implements IPromotionStrategy {
    @Override
    public void doPromotion() {
        System.out.println("返現,直接打款到支付寶帳號");
    }
}

package com.wf.strategy.demo.promotion;

/**
 * @ClassName CouponStrategy
 * @Description 優惠券促銷方式
 * @Author wf
 * @Date 2020/6/18 10:33
 * @Version 1.0
 */
public class CouponStrategy implements IPromotionStrategy {
    @Override
    public void doPromotion() {
        System.out.println("使用優惠券抵扣");
    }
}
package com.wf.strategy.demo.promotion;

/**
 * @ClassName EmptyStrategy
 * @Description 無優惠購買
 * @Author wf
 * @Date 2020/6/18 10:37
 * @Version 1.0
 */
public class EmptyStrategy implements IPromotionStrategy {
    @Override
    public void doPromotion() {
        System.out.println("原價購買,無優惠");
    }
}

 

3.測試類
 public static void main(String[] args) {
        String promotion = "";
        IPromotionStrategy strategy = null;
        if("團購".equals(promotion)){
            strategy = new GroupPurchaseStrategy();
        }else if("".equals(promotion)){
            //...
            strategy = new EmptyStrategy();
        }
        strategy.doPromotion();
    }

 

測試結果:

 

 

 說明:

  這里並沒有使用策略模式,而是通過分支判斷,多態的方式來實現。

4.策略模式運用---增加上下文對象
package com.wf.strategy.demo.promotion;

/**
 * @ClassName PromotionActivity
 * @Description 上下文對象,促銷活動
 * @Author wf
 * @Date 2020/6/18 10:45
 * @Version 1.0
 */
public class PromotionActivity {
    private IPromotionStrategy strategy;

    public PromotionActivity(IPromotionStrategy strategy) {
        this.strategy = strategy;
    }
    public void execute(){
        strategy.doPromotion();
    }
}

 

修改測試代碼如下:

  public static void main(String[] args) {
        String promotion = "";
        PromotionActivity activity = null;
        if("團購".equals(promotion)){
            activity = new PromotionActivity(new GroupPurchaseStrategy());
        }else if("".equals(promotion)){
            //...
            activity = new PromotionActivity(new EmptyStrategy());
        }
        activity.execute();

        //說明:
        //這里仍然需要進行if...else分支判斷,代碼仍然不夠優雅
        //解決這個問題,通常使用策略模式 + 簡單工廠模式
    }

 

說明:

//這里仍然需要進行if...else分支判斷,代碼仍然不夠優雅
//解決這個問題,通常使用策略模式 + 簡單工廠模式
5.引入簡單工廠優化
package com.wf.strategy.demo.promotion;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @ClassName PromotionStrategyFactory
 * @Description 上下文對象創建工廠
 * @Author wf
 * @Date 2020/6/18 10:52
 * @Version 1.0
 */
public class PromotionStrategyFactory {
    private static Map<String,IPromotionStrategy> promotionStrategyMap = new HashMap<String,IPromotionStrategy>();

    private static final IPromotionStrategy EMPTY = new EmptyStrategy();
    static{
        promotionStrategyMap.put(PromotionKey.COUPON,new CouponStrategy());
        promotionStrategyMap.put(PromotionKey.CASH_ROLLBACK,new CashRollbackStrategy());
        promotionStrategyMap.put(PromotionKey.GROUP_PURCHASE,new GroupPurchaseStrategy());
    }
    private PromotionStrategyFactory(){}
    public static IPromotionStrategy getPromotionStrategy(String promotionKey){
        IPromotionStrategy strategy = promotionStrategyMap.get(promotionKey);
        return strategy == null ? EMPTY : strategy;
    }

    //定義內部接口,定義key
    private interface PromotionKey{
        String COUPON = "COUPON";
        String CASH_ROLLBACK = "CASH_ROLLBACK";
        String GROUP_PURCHASE = "GROUP_PURCHASE";

    }
    public Set<String> getPromotionKeys(){
        return promotionStrategyMap.keySet();
    }
}

 

修改測試類:

 public static void main(String[] args) {
        String promotionKey = "COUPON";
        IPromotionStrategy strategy = PromotionStrategyFactory.getPromotionStrategy(promotionKey);
        strategy.doPromotion();
    }

 

測試結果如下:

說明:

分支判斷,移到工廠類中去處理。

10.1.3.2.系統類圖

 

 

 

10.1.4.策略模式的實現示例之支付方式選擇案例

10.1.4.1.代碼實現
1.頂層接口
package com.wf.strategy.demo.pay.payport;

import com.wf.strategy.demo.pay.MsgResult;

/**
 * @ClassName Payment
 * @Description 支付抽象接口
 * @Author wf
 * @Date 2020/6/18 11:31
 * @Version 1.0
 */
public abstract class Payment {
    public abstract String getName();
    public MsgResult pay(String uid, double amount){
        //查詢余額是否足夠
        if(queryBalance(uid) < amount){
            return new MsgResult(500,"支付失敗","余額不足");
        }
        return new MsgResult(200,"支付成功","支付金額:"+amount);
    }

    protected abstract double queryBalance(String uid);
}

 

2.子類實現
package com.wf.strategy.demo.pay.payport;

/**
 * @ClassName WechatPay
 * @Description 微信支付
 * @Author wf
 * @Date 2020/6/18 14:27
 * @Version 1.0
 */
public class WechatPay extends Payment {
    @Override
    public String getName() {
        return "微信支付";
    }

    @Override
    protected double queryBalance(String uid) {
        return 500;
    }
}
package com.wf.strategy.demo.pay.payport;

/**
 * @ClassName JDPay
 * @Description 京東白條支付
 * @Author wf
 * @Date 2020/6/18 14:25
 * @Version 1.0
 */
public class JDPay extends Payment {
    @Override
    public String getName() {
        return "京東白條";
    }

    @Override
    protected double queryBalance(String uid) {
        return 500;
    }
}
package com.wf.strategy.demo.pay.payport;

/**
 * @ClassName BankUnionPay
 * @Description 銀聯支付
 * @Author wf
 * @Date 2020/6/18 14:28
 * @Version 1.0
 */
public class BankUnionPay extends Payment {
    @Override
    public String getName() {
        return "銀聯支付";
    }

    @Override
    protected double queryBalance(String uid) {
        return 120;
    }
}
package com.wf.strategy.demo.pay.payport;

/**
 * @ClassName AliPay
 * @Description 支付寶支付
 * @Author wf
 * @Date 2020/6/18 14:23
 * @Version 1.0
 */
public class AliPay extends Payment {
    @Override
    public String getName() {
        return "支付寶";
    }

    @Override
    protected double queryBalance(String uid) {
        return 900;
    }
}

 

3.訂單類
package com.wf.strategy.demo.pay;

import com.wf.strategy.demo.pay.payport.PayStrategy;
import com.wf.strategy.demo.pay.payport.Payment;

/**
 * @ClassName Order
 * @Description 訂單類
 * @Author wf
 * @Date 2020/6/18 14:29
 * @Version 1.0
 */
public class Order {
    private String uid;
    private String orderId;
    private double amount;

    public Order(String uid, String orderId, double amount) {
        this.uid = uid;
        this.orderId = orderId;
        this.amount = amount;
    }
    public MsgResult pay(){
        return pay(PayStrategy.DEFAULT_PAY);
    }
    public MsgResult pay(String payKey){
        Payment payment = PayStrategy.get(payKey);
        System.out.println("歡迎使用:"+payment.getName());
        System.out.println("本次交易金額為:"+amount+",開始扣款");
        return payment.pay(uid,amount);
    }
}

 

4.上下文對象
package com.wf.strategy.demo.pay.payport;

import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName PayStrategy
 * @Description 支付策略,上下文對象
 * @Author wf
 * @Date 2020/6/18 14:32
 * @Version 1.0
 */
public class PayStrategy {
    public static final String ALI_PAY = "AliPay";
    public static final String JD_PAY = "JdPay";
    public static final String WECHAT_PAY = "WechatPay";
    public static final String BANKUINION_PAY = "BankUnionPay";
    public static final String DEFAULT_PAY = "AliPay";

    private static Map<String,Payment> strategyMap = new HashMap<String, Payment>();
    static {
        strategyMap.put(ALI_PAY,new AliPay());
        strategyMap.put(JD_PAY,new JDPay());
        strategyMap.put(WECHAT_PAY,new WechatPay());
        strategyMap.put(BANKUINION_PAY,new BankUnionPay());
    }

    public static Payment get(String payKey){
        if(!strategyMap.containsKey(payKey)){
            return strategyMap.get(DEFAULT_PAY);
        }
        return strategyMap.get(payKey);
    }
}

 

5.結果集po
package com.wf.strategy.demo.pay;

/**
 * @ClassName MsgResult
 * @Description 結果集
 * @Author wf
 * @Date 2020/6/18 11:36
 * @Version 1.0
 */
public class MsgResult {
    private int code;
    private Object data;
    private String msg;

    public MsgResult(int code,  String msg, Object data) {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "MsgResult{" +
                "code='" + code + '\'' +
                ", data=" + data +
                ", msg='" + msg + '\'' +
                '}';
    }
}

 

6.測試類
package com.wf.strategy.demo.pay;

import com.sun.org.apache.xpath.internal.operations.Or;
import com.wf.strategy.demo.pay.payport.PayStrategy;

/**
 * @ClassName Test
 * @Description 測試類
 * @Author wf
 * @Date 2020/6/18 14:43
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        Order order = new Order("1","20200618012222",324.5);
        System.out.println(order.pay(PayStrategy.BANKUINION_PAY));
    }
}

 

測試結果如下:

 

 

 10.1.4.2.類圖設計

 

 

10.1.5.策略模式在源碼中應用

10.1.5.1.jdk中Comparator接口
int compare(T o1, T o2);

 

表示列表中前后兩個元素進行比較,如果返回值大於0,表示升序。

 

它的實現類,在Arrays中parallelSort方法有定義:

public static <T> void parallelSort(T[] a, Comparator<? super T> cmp) {
        if (cmp == null)
            cmp = NaturalOrder.INSTANCE;  //默認策略 int n = a.length, p, g;
        if (n <= MIN_ARRAY_SORT_GRAN ||
            (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
            TimSort.sort(a, 0, n, cmp, null, 0, 0);
        else
            new ArraysParallelSortHelpers.FJObject.Sorter<T>
                (null, a,
                 (T[])Array.newInstance(a.getClass().getComponentType(), n),
                 0, n, 0, ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
                 MIN_ARRAY_SORT_GRAN : g, cmp).invoke();
    }

 

默認策略中調用:

 static final class NaturalOrder implements Comparator<Object> {
        @SuppressWarnings("unchecked")
        public int compare(Object first, Object second) {
            return ((Comparable<Object>)first).compareTo(second);
        }
        static final NaturalOrder INSTANCE = new NaturalOrder();
    }

 

10.1.5.2.spring中Resource接口
10.1.5.3.spring中InstantiationStrategy接口

 

 

10.1.6.策略模式使用總結

10.1.6.1.優缺點總結

優點:

  符合開閉原則

  避免使用多重條件判斷,如:if...else if...,switch

  使用策略模式可以提高算法的保密性和安全性。

缺點:

  客戶端必須要知道所有的策略,並且自行選擇使用哪一個策略。

  可以通過字典表配置成枚舉參數,當用戶點擊時,彈出所有策略。任選一個。

 

  代碼中會增加非常多的策略類,增加維護難度 。

 

1.10.2.責任鏈模式詳解

時長:46min

10.2.1.責任鏈模式定義

定義

  Chain of Responsibility Pattern,是將鏈中每一個節點看作是一個對象,每個節點處理的請求均不同,且內部自動維護一個

下一節點的對象。當一個請求從鏈式的首端發現時,會沿着鏈的路徑依次傳遞給每一個節點對象,直至對象處理完這個請求為止。

  屬於行為型模式。

10.2.1.1.在生活中應用

工作流中審批流程

過五關,斬六將

10.2.1.2.責任鏈模式的適用場景

1.多個對象處理同一個請求,但具體哪個對象處理則在運行時動態決定

2.在不明確指定接收者的前提下,向多個對象中的一個提交一個請求

3.可動態地指定一組對象處理請求

10.2.2.責任鏈模式通用設計

10.2.2.1.類圖設計

 

 

 10.2.2.2.代碼實現
1.頂層抽象處理器
package com.wf.chain.general;

/**
 * @ClassName Handler
 * @Description 處理器
 * @Author wf
 * @Date 2020/6/18 15:52
 * @Version 1.0
 */
public abstract class Handler {
    protected Handler nextHandle;

    public void setNextHandle(Handler nextHandle) {
        this.nextHandle = nextHandle;
    }
    public abstract void handleRequest(String request);
}

 

2.處理器子類
package com.wf.chain.general;

/**
 * @ClassName ConcreteHandlerA
 * @Description 實現子類A
 * @Author wf
 * @Date 2020/6/18 15:54
 * @Version 1.0
 */
public class ConcreteHandlerA extends Handler {
    @Override
    public void handleRequest(String request) {
        if("requestA".equals(request)){
            System.out.println(this.getClass().getSimpleName() + " deal with request:" + request);
            return;
        }
        if(this.nextHandle != null){
            this.nextHandle.handleRequest(request);
        }
    }
}
package com.wf.chain.general;

/**
 * @ClassName ConcreteHandlerB
 * @Description 實現子類B
 * @Author wf
 * @Date 2020/6/18 15:54
 * @Version 1.0
 */
public class ConcreteHandlerB extends Handler {
    @Override
    public void handleRequest(String request) {
        if("requestB".equals(request)){
            System.out.println(this.getClass().getSimpleName() + " deal with request:" + request);
            return;
        }
        if(this.nextHandle != null){
            this.nextHandle.handleRequest(request);
        }
    }
}

 

3.測試類
package com.wf.chain.general;

/**
 * @ClassName Test
 * @Description 測試類
 * @Author wf
 * @Date 2020/6/18 15:55
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        Handler handlerA = new ConcreteHandlerA();
        Handler handlerB = new ConcreteHandlerB();
        //設置鏈關系,先A后B
        handlerA.setNextHandle(handlerB);
        handlerA.handleRequest("requestB");
    }
}

 

測試結果:

 

 

 

10.2.3.責任鏈模式應用之用戶登錄校驗

10.2.3.1.代碼實現
1.員工bean定義
package com.wf.chain.demo.auth;

/**
 * @ClassName Member
 * @Description 員工類,業務bean
 * @Author wf
 * @Date 2020/6/18 16:07
 * @Version 1.0
 */
public class Member {
    //登錄名
    private String loginName;
    //登錄密碼
    private String loginPass;
    //員工權限
    private String roleName;

    public Member(String loginName, String loginPass) {
        this.loginName = loginName;
        this.loginPass = loginPass;
    }

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getLoginPass() {
        return loginPass;
    }

    public void setLoginPass(String loginPass) {
        this.loginPass = loginPass;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
}

 

2.登錄服務
package com.wf.chain.demo.auth.old;

import org.springframework.util.StringUtils;

/**
 * @ClassName MemberService
 * @Description 員工類服務接口
 * @Author wf
 * @Date 2020/6/18 16:07
 * @Version 1.0
 */
public class MemberService {

    public void login(String loginName, String loginPass){
        //入參判空
        if(StringUtils.isEmpty(loginName) || StringUtils.isEmpty(loginPass)){
            System.out.println("用戶名和密碼為空");
            return;
        }
        System.out.println("用戶名和密碼不為空,可以往下執行了");

        Member member = checkExist(loginName,loginPass);
        if(null == member){
            System.out.println("用戶不存在");
            return;
        }
        //用戶名存在,表示可登錄
        System.out.println("登錄成功");
        if(!"管理員".equals(member.getRoleName())){
            System.out.println("您不是管理員,沒有操作權限");
        }
        System.out.println("允許操作");
    }

    private Member checkExist(String loginName, String loginPass) {
        Member member = new Member(loginName,loginPass);
        member.setRoleName("管理員");
        return member;
    }

}

 

說明:

  login方法中,需要進行一系列的校驗。入參判空校驗,用戶名存在性校驗,用戶權限校驗。。。

  這些校驗可能會非常復雜,寫在一個方法中,方法的職責過重,需要進行拆分。

 

  因此,考慮將幾部分校驗邏輯進行拆分。就將3個處理,分別拆分,串聯成一個責任鏈。

3.使用責任鏈模式優化代碼

【1】定義頂層抽象handler

package com.wf.chain.demo.auth.optimize;

import com.wf.chain.demo.auth.Member;

/**
 * @ClassName Handler
 * @Description 定義抽象處理器
 * @Author wf
 * @Date 2020/6/18 16:28
 * @Version 1.0
 */
public abstract class Handler {
    protected Handler next;

    public void next(Handler next) {
        this.next = next;
    }

    public abstract void doHandle(Member member);
}

 

【2】定義處理實現子類 

package com.wf.chain.demo.auth.optimize;

import com.wf.chain.demo.auth.Member;
import org.springframework.util.StringUtils;

/**
 * @ClassName ValidateHandler
 * @Description 入參判空處理
 * @Author wf
 * @Date 2020/6/18 16:30
 * @Version 1.0
 */
public class ValidateHandler extends Handler {

    @Override
    public void doHandle(Member member) {
        //入參判空
        if(StringUtils.isEmpty(member.getLoginName()) || StringUtils.isEmpty(member.getLoginPass())){
            System.out.println("用戶名和密碼為空");
            return;
        }
        System.out.println("用戶名和密碼不為空,可以往下執行了");

        //向下傳遞
        next.doHandle(member);
    }
}
package com.wf.chain.demo.auth.optimize;

import com.wf.chain.demo.auth.Member;

/**
 * @ClassName LoginHandler
 * @Description 登錄校驗,判斷用戶存在性
 * @Author wf
 * @Date 2020/6/18 16:35
 * @Version 1.0
 */
public class LoginHandler extends Handler {
    @Override
    public void doHandle(Member member) {
        Member existMember = checkExist(member.getLoginName(),member.getLoginPass());
        if(null == existMember){
            System.out.println("用戶不存在");
            return;
        }
        //用戶名存在,表示可登錄
        System.out.println("登錄成功");

        next.doHandle(existMember);
    }

    private Member checkExist(String loginName, String loginPass) {
        Member member = new Member(loginName,loginPass);
        member.setRoleName("管理員");
        return member;
    }
}
package com.wf.chain.demo.auth.optimize;

import com.wf.chain.demo.auth.Member;

/**
 * @ClassName AuthHandler
 * @Description 權限校驗處理器
 * @Author wf
 * @Date 2020/6/18 16:38
 * @Version 1.0
 */
public class AuthHandler extends Handler {
    @Override
    public void doHandle(Member member) {
        if(!"管理員".equals(member.getRoleName())){
            System.out.println("您不是管理員,沒有操作權限");
       return; } System.out.println(
"允許操作"); } }

 

【3】優化Service服務邏輯

package com.wf.chain.demo.auth.optimize;

import com.wf.chain.demo.auth.Member;
import org.springframework.util.StringUtils;

/**
 * @ClassName MemberService
 * @Description 員工類服務接口
 * @Author wf
 * @Date 2020/6/18 16:07
 * @Version 1.0
 */
public class MemberService {

    public void login(String loginName, String loginPass){
        //通過責任鏈來處理
        Handler validateHandler = new ValidateHandler();
        Handler loginHandler = new LoginHandler();
        Handler authHandler = new AuthHandler();

        loginHandler.next(authHandler);
        validateHandler.next(loginHandler);

        validateHandler.doHandle(new Member(loginName,loginPass));
    }

}

 

【4】.測試類

package com.wf.chain.demo.auth;

import com.wf.chain.demo.auth.optimize.MemberService;

/**
 * @ClassName Test
 * @Description 測試類
 * @Author wf
 * @Date 2020/6/18 16:43
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        MemberService service = new MemberService();
        service.login("wf","@g;aj;132");
    }
}

 

測試結果如下:

 

 

 說明:

  這里引入優化后的service服務,達到一樣的效果,代碼優雅度提升太多。

  但是,service中使用next方法來,建立責任鏈關系。讓人很頭昏,難以理解。

  希望可以進一步優化。

  在建造者模式中,我們去體會到了鏈式編程的好處,可以用來做優化。使用add來建立關系,就很容易理解了。

4.責任鏈模式結合建造者模式優化代碼

【1】頂層Hanlder修改

package com.wf.chain.demo.auth.builderchain;

import com.wf.chain.demo.auth.Member;

/**
 * @ClassName Handler
 * @Description 定義抽象處理器
 * @Author wf
 * @Date 2020/6/18 16:28
 * @Version 1.0
 */
public abstract class Handler<T> {
    protected Handler next;

    public void next(Handler next) {
        this.next = next;
    }

    public abstract void doHandle(Member member);

    public static class Builder<T> {
        private Handler<T> head;
        private Handler<T> tail;

        public Builder<T> addHandler(Handler handler){
            if(this.head == null){
                this.head = this.tail = handler;
                return this;
            }
            //頭部已有元素,從尾部開始追加
            this.tail.next(handler);//添加下一個元素
            this.tail = handler;    //指針位置移動一位,同時head也指向下一個
            return this;
        }
        public Handler<T> build(){
            return this.head;
        }
    }
}

 

【2】處理器子類實現不變

【3】service服務修改

package com.wf.chain.demo.auth.builderchain;

import com.wf.chain.demo.auth.Member;

/**
 * @ClassName MemberService
 * @Description 員工類服務接口
 * @Author wf
 * @Date 2020/6/18 16:07
 * @Version 1.0
 */
public class MemberService {

    public void login(String loginName, String loginPass){
        //通過責任鏈來處理
        Handler.Builder builder = new Handler.Builder();
        builder.addHandler(new ValidateHandler())
                .addHandler(new LoginHandler())
                .addHandler(new AuthHandler());

        builder.build().doHandle(new Member(loginName,loginPass));
    }

}

 

說明:

  可以看到,login中的處理邏輯,顯得很清楚,易於理解。

【4】測試類修改

package com.wf.chain.demo.auth.builderchain;


/**
 * @ClassName Test
 * @Description 測試類
 * @Author wf
 * @Date 2020/6/18 16:43
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        MemberService service = new MemberService();
        service.login("wf","@g;aj;132");
//        service.login("wf",null);
//        service.login("wf","");
    }
}

 

說明:

  這個優化過程,重點在於Handler的處理。

  對於鏈表的處理,在框架中多處理為雙向鏈表,如下所示代碼:

public abstract class Handler<T> {
    protected Handler next;

    public void next(Handler next) {
        this.next = next;
    }

    public abstract void doHandle(Member member);

    public static class Builder<T> {
        private Handler<T> head;
        private Handler<T> tail;

        public Builder<T> addHandler(Handler handler){
            do {
                if (this.head == null) {
                    this.head = this.tail = handler;
                    //return this;
                    break;
                }
                //頭部已有元素,從尾部開始追加
                this.tail.next(handler);//添加下一個元素
                this.tail = handler;    //指針位置移動一位,同時head也指向下一個
            }while (false); //真正在框架中,如果是雙向鏈表,會判斷是否已經到達尾部
                return this;
        }
        public Handler<T> build(){
            return this.head;
        }
    }
}

 

10.2.4.責任鏈模式在源碼中的應用

10.2.4.1.servlet中Filter

需要引入mvn依賴:

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
  <scope>provided</scope>
</dependency>

 

搜索:Filter,如下所示:

package javax.servlet;

import java.io.IOException;

public interface Filter {
    void init(FilterConfig var1) throws ServletException;

    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    void destroy();
}

 

doFilter中傳參FilterChain,源碼如下:

package javax.servlet;

import java.io.IOException;

public interface FilterChain {
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}

 

 

10.2.4.2.netty中ChannelPipeline

需要引入mvn依賴包:

<dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>4.1.42.Final</version>
    </dependency>

 

找到默認實現DefaultChannelPipeline,如下所示:

 final AbstractChannelHandlerContext head;
    final AbstractChannelHandlerContext tail;

 

ChannelHandler

在權限安全校驗框架中,大量使用責任鏈模式。如:

spring security/apache shiro

10.2.4.責任鏈模式使用總結

10.2.4.1.優缺點總結

優點:

  1.將請求與處理解耦

  2.請求處理者【節點對象】只需要關注自己感興趣的請求進行處理即可。對於不感興趣的請求,

直接轉發給下一節點對象。

  3.具備鏈式傳遞處理請求功能,請求發送者無需知曉鏈路結構,只需等待請求處理結果。

  4.鏈路結構靈活,可以通過改變鏈路結構動態地新增或刪減責任。

  5.易於擴展新的請求處理類(節點),符合開閉原則。

 

缺點:

  1.責任鏈太長或者處理時間過長,會影響整體性能

  2.如果節點對象存在循環引用,會造成死循環,導致系統崩潰。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM