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.如果節點對象存在循環引用,會造成死循環,導致系統崩潰。