使多個對像都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係,將這些對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個對象能處理它為止,傳遞鏈中的這些對象就叫節點。
在實際的運用當中,責任鍊模式也常常用在重構繁瑣的if… if else… 中,這和之前學習的策略模式有相似之處,相比策略,責任鍊模式邏輯上更加繁瑣,但是責任鍊模式有著更大的彈性空間。
假如上一個環節的結果,是需要交給下一個環節來繼續加工,那麼此時責任鍊模式是更加適合處理的。
接下來假設一個需求
一個電商網站,
用戶交500定金且定金已付時,可享受500優惠券且不受貨物數量限制;
用戶交200定金且定金已付時,可享受500優惠券且不受貨物數量限制;
用戶不交定金時受貨物數量限制,有貨時原價買,無貨時則無法買。
var buyOrder = function(orederType, pay, stock){
if(orederType == 1){
if(pay){
console.log('500优惠券');
} else {
if(stock > 0){
console.log('普通购物页面');
} else {
console.log('已无货');
}
}
} else if(orederType == 2){
if(pay){
console.log('200优惠券');
} else {
if(stock > 0){
console.log('普通购物页面');
} else {
console.log('已无货');
}
}
} else if(orederType == 3){
if(stock > 0){
console.log('普通购物页面');
} else {
console.log('已无货');
}
}
}
buyOrder(1, true, 600)
接下來調用它
//3个订单函数 ,它们都是节点函数
const order500 = function (orderType, pay, stock) {
if (orderType == '1' && pay == true) {
console.log('500优惠券');
} else {
return 'nextSuccessor'; // 不管下一個節點是誰,反正就是把請求往後傳遞
}
}
const order200 = function (orderType, pay, stock) {
if (orderType == '2' && pay == true) {
console.log('200优惠券');
} else {
return 'nextSuccessor'; // 不管下一個節點是誰,反正就是把請求往後傳遞
}
}
const orderNormal = function (orderType, pay, stock) {
if (stock > 0) {
console.log('普通购物页面');
} else {
console.log('已无货');
}
}
//把3個訂單函數分別包裝成職責鏈的節點
const chainOrder500 = new Chain(order500)
const chainOrder200 = new Chain(order200)
const chainOrderNormal = new Chain(orderNormal)
//然後指定節點在職責鏈中的順序
chainOrder500.setNextSuccessor(chainOrder200)
chainOrder200.setNextSuccessor(chainOrderNormal)
//最後把請求傳遞給第一個節點,開啟職責鏈模式傳遞
chainOrder500.handleRequest(1, true, 500) //500优惠券
chainOrder500.handleRequest(3, true, 20) //普通购物页面
chainOrder500.handleRequest(3, true, 0) //已无货
之後來分享一下另外一個例子:
有一個活動的H5頁面,頁面被用在多個瀏覽器環境,比如微信、QQ、自己開發的app、其他第三方的app等等等等。由於後台接口對不同環境登錄態的處理不同,需要判斷不同環境,取cookie裡面的不同值,傳不同的登錄態token,有時候還要針對某些特殊環境,對網絡請求有一些特殊處理。
以下為typescript的例子,可以看到BaseHandler 這個類別是抽象類,表示它必須的依附在另外一個實例當中
// 请求拦截器责任链节点抽象类
export default abstract class BaseHandler {
// 下一个节点
private nextHandler: BaseHandler | null = null;
// 设置下一个节点
public setNextHandler (handler: BaseHandler): BaseHandler {
this.nextHandler = handler;
return this.nextHandler;
}
// 调用该节点的方法处理请求,处理后,调用下一节点继续处理
public handleRequest (config: any): void {
// 当前节点的处理
if (this.checkHandler(config)) {
this.handler(config);
}
// 继续执行下一个节点
if (this.nextHandler) {
this.nextHandler.handlerRequest(config);
}
}
// 调用该节点的方法处理请求,有一个节点处理就直接退出(互斥的)
public handleRequestOnce(config: any): void {
// 当前节点的处理
if (this.checkHandle(config)) {
this.handler(config);
} else if (this.nextHandler) {
this.nextHandler.handleRequestOnce(config);
}
}
// 判断是否在这个节点处理
abstract checkHandle(config: any): boolean;
// 处理的方式
abstract handler(config: any): void;
}
/*
作者:lucio来全学了
链接:https://juejin.cn/post/7055149674650402824
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*/
import BaseHandler from '../../base-handler';
import { getUrlPara } from '../../../url';
import { env } from '../../../env';
// 处理小程序内嵌H5的请求参数
export default class WechatMpParamsHandler extends BaseHandler {
// 在王者人生小程序,且为QQ登录,才用小程序登录态,其他情况下都用微信登录态
checkHandle(): boolean {
return env.isMiniProgram;
}
handler(config: any): any {
if (getUrlPara('_ticket')) {
config.params._ticket = getUrlPara('_ticket');
}
return config;
}
}
/*
作者:lucio来全学了
链接:https://juejin.cn/post/7055149674650402824
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处
*/
最後在axios攔截器實現調用
// 请求拦截器
const requestInterceptor = function (config) {
const requestHandlerChain = new UrlInterceptor(); // 请求的责任链节点:CGI的URL处理
requestHandlerChain.setNextHandler(new ParamsInterceptor()); // 请求的责任链节点:CGI的参数处理
requestHandlerChain.handleRequest(config);
return config;
};
// 响应拦截器
const responseInterceptor = (response) => {
const responseHandlerChain = new ParseDataInterceptor(); // 返回数据的责任链节点:解析新老框架的数据
responseHandlerChain.setNextHandler(new LoginInfoInterceptor()) // 返回数据的责任链节点:处理登录信息
.setNextHandler(new ErrorToastInterceptor()); // 返回数据的责任链节点:处理错误提示
responseHandlerChain.handleRequest(response);
return response;
};
// 请求拦截器
axiosInstance.interceptors.request.use(requestInterceptor, error => Promise.reject(error));
// 响应拦截器
axiosInstance.interceptors.response.use(responseInterceptor, error => Promise.reject(error));
/*
作者:lucio来全学了
链接:https://juejin.cn/post/7055149674650402824
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*/
以上待續…