自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

聊一聊責(zé)任鏈模式

開發(fā) 前端
責(zé)任鏈模式(Chain of Responsibility Pattern)是將鏈中每一個(gè)節(jié)點(diǎn)看作是一個(gè)對(duì)象,每個(gè)節(jié)點(diǎn)處理的請(qǐng)求均不同,且內(nèi)部自動(dòng)維護(hù)一個(gè)下一節(jié)點(diǎn)對(duì)象。當(dāng)一個(gè)請(qǐng)求從鏈?zhǔn)降氖锥税l(fā)出時(shí),會(huì)沿著鏈的路徑依次傳遞給每一個(gè)節(jié)點(diǎn)對(duì)象,直至有對(duì)象處理這個(gè)請(qǐng)求為止,屬于行為型模式。

一、概述

責(zé)任鏈模式(Chain of Responsibility Pattern)是將鏈中每一個(gè)節(jié)點(diǎn)看作是一個(gè)對(duì)象,每個(gè)節(jié)點(diǎn)處理的請(qǐng)求均不同,且內(nèi)部自動(dòng)維護(hù)一個(gè)下一節(jié)點(diǎn)對(duì)象。當(dāng)一個(gè)請(qǐng)求從鏈?zhǔn)降氖锥税l(fā)出時(shí),會(huì)沿著鏈的路徑依次傳遞給每一個(gè)節(jié)點(diǎn)對(duì)象,直至有對(duì)象處理這個(gè)請(qǐng)求為止,屬于行為型模式。下面放一張足球比賽的圖,通過層層傳遞,最終射門。通過這張圖,可以更好的理解責(zé)任鏈模式。

圖片

二、入門案例

2.1 類圖

圖片

2.2 基礎(chǔ)類介紹

抽象接口RequestHandler

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/10/25 13:41
* @description
*/
public interface RequestHandler {

void doHandler(String req);
}

抽象類BaseRequestHandler

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/10/25 13:45
* @description
*/
public abstract class BaseRequestHandler implements RequestHandler {

protected RequestHandler next;

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

具體處理類AHandler

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/10/25 14:00
* @description
*/
public class AHandler extends BaseRequestHandler {

@Override
public void doHandler(String req){
// 處理自己的業(yè)務(wù)邏輯
System.out.println("A中處理自己的邏輯");
// 傳遞給下個(gè)類(若鏈路中還有下個(gè)處理類)
if (next != null) {
next.doHandler(req);
}
}
}

當(dāng)然還有具體的處理類B、C等等,這里不展開贅述。

 使用類Client

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/10/25 14:06
* @description
*/
public class Client {
public static void main(String[] args){
BaseRequestHandler a = new AHandler();
BaseRequestHandler b = new BHandler();
BaseRequestHandler c = new CHandler();
a.next(b);
b.next(c);
a.doHandler("鏈路待處理的數(shù)據(jù)");
}
}

2.3 處理流程圖

圖片

三、應(yīng)用場(chǎng)景

3.1 場(chǎng)景舉例

場(chǎng)景一

前兩年,在一家金融公司待過一段時(shí)間,其中就有一個(gè)業(yè)務(wù)場(chǎng)景:一筆訂單進(jìn)來,會(huì)先在后臺(tái)通過初審人員進(jìn)行審批,初審不通過,訂單流程結(jié)束。初審?fù)ㄟ^以后,會(huì)轉(zhuǎn)給終審人員進(jìn)行審批,不通過,流程結(jié)束;通過,流轉(zhuǎn)到下個(gè)業(yè)務(wù)場(chǎng)景。對(duì)于這塊業(yè)務(wù)代碼,之前一代目是一個(gè)叫知了的同事,他擼起袖子就是干,一套if-else干到底。后來,技術(shù)老大CodeReview,點(diǎn)名要求改掉這塊。于是乎,想到用用設(shè)計(jì)模式吧,然后就噼里啪啦一頓改。(當(dāng)然,比較復(fù)雜的情況,還是可以用工作流來處理這個(gè)場(chǎng)景,當(dāng)時(shí)礙于時(shí)間成本,也就放棄了)。

場(chǎng)景二

上家公司對(duì)接甲方爸爸的時(shí)候,對(duì)方會(huì)調(diào)用我們接口,將數(shù)據(jù)同步過來。同樣,我們需要將處理好的數(shù)據(jù),傳給他們。由于雙方傳輸數(shù)據(jù)都是加密傳輸,所以在接受他們數(shù)據(jù)之前,需要對(duì)數(shù)據(jù)進(jìn)行解密,驗(yàn)簽,參數(shù)校驗(yàn)等操作。同樣,我們給他們傳數(shù)據(jù)也需要進(jìn)行加簽,加密操作。

具體案例

話不多說,對(duì)于場(chǎng)景二,我來放一些偽代碼,跟大家一起探討下。1、一切從注解開始,我這里自定義了一個(gè)注解@Duty?,這個(gè)注解有spring的@Component注解,也就是標(biāo)記了這個(gè)自定義注解的類,都是交給spring的bean容器去管理。注解中,有兩個(gè)屬性:1.type,定義相同的type類型的bean,會(huì)被放到一個(gè)責(zé)任鏈集合中。2.order,同一個(gè)責(zé)任鏈集合中,bean的排序,數(shù)值越小,會(huì)放到鏈路最先的位置,優(yōu)先處理。

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/10/25 16:11
* @description
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Service
public @interface Duty {
/**
* 標(biāo)記具體業(yè)務(wù)場(chǎng)景
* @return
*/
String type() default "";

/**
* 排序:數(shù)值越小,排序越前
* @return
*/
int order() default 0;
}

2、定義一個(gè)頂層的抽象接口IHandler,傳入2個(gè)泛型參數(shù),供后續(xù)自定義。

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/10/25 15:31
* @description 責(zé)任鏈頂層抽象類
*/
public interface IHandler<T, R> {
/**
* 抽象處理類
* @param t
* @return
*/
R handle(T t);
}

3、定義一個(gè)責(zé)任鏈bean的管理類HandleChainManager,用來存放不同業(yè)務(wù)下的責(zé)任鏈路集合。在該類中,有一個(gè)Map和兩個(gè)方法。

handleMap:這個(gè)map會(huì)存放責(zé)任鏈路中,具體的執(zhí)行類,key是注解@Duty?中定義的type值,value是標(biāo)記了@Duty注解的bean集合,也就是具體的執(zhí)行類集合。

setHandleMap:傳入具體執(zhí)行bean的集合,存放在map中。

executeHandle:從map中找到具體的執(zhí)行bean集合,并依次執(zhí)行。

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/10/25 16:00
* @description 責(zé)任鏈管理類
*/
public class HandleChainManager {
/**
* 存放責(zé)任鏈路上的具體處理類
* k-具體業(yè)務(wù)場(chǎng)景名稱
* v-具體業(yè)務(wù)場(chǎng)景下的責(zé)任鏈路集合
*/
private Map<String, List<IHandler>> handleMap;

/**
* 存放系統(tǒng)中責(zé)任鏈具體處理類
* @param handlerList
*/
public void setHandleMap(List<IHandler> handlerList){
handleMap = handlerList
.stream()
.sorted(Comparator.comparingInt(h -> AnnotationUtils.findAnnotation(h.getClass(), Duty.class).order()))
.collect(Collectors.groupingBy(handler -> AnnotationUtils.findAnnotation(handler.getClass(), Duty.class).type()));
}

/**
* 執(zhí)行具體業(yè)務(wù)場(chǎng)景中的責(zé)任鏈集合
* @param type 對(duì)應(yīng)@Duty注解中的type,可以定義為具體業(yè)務(wù)場(chǎng)景
* @param t 被執(zhí)行的參數(shù)
*/
public <T, R> R executeHandle(String type, T t){
List<IHandler> handlers = handleMap.get(type);
R r = null;
if (CollectionUtil.isNotEmpty(handlers)) {
for (IHandler<T, R> handler : handlers) {
r = handler.handle(t);
}
}
return r;
}
}

4、定義一個(gè)配置類PatternConfiguration?,用于裝配上面的責(zé)任鏈管理器HandleChainManager。

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/10/25 15:35
* @description 設(shè)計(jì)模式配置類
*/
@Configuration
public class PatternConfiguration {

@Bean
public HandleChainManager handlerChainExecute(List<IHandler> handlers){
HandleChainManager handleChainManager = new HandleChainManager();
handleChainManager.setHandleMap(handlers);
return handleChainManager;
}

}

5、具體的處理類:SignChainHandler、EncryptionChainHandler、RequestChainHandler?,這里我以SignChainHandler?為例。在具體處理類上標(biāo)記自定義注解@Duty?,該類會(huì)被注入到bean容器中,實(shí)現(xiàn)IHandler接口,只需關(guān)心自己的handle方法,處理具體的業(yè)務(wù)邏輯。

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/10/25 15:31
* @description 加簽類
*/
@Duty(type = BusinessConstants.REQUEST, order = 1)
public class SignChainHandler implements IHandler<String, String> {
/**
* 處理加簽邏輯
* @param s
* @return
*/
@Override
public String handle(String s){
// 加簽邏輯
System.out.println("甲方爸爸要求加簽");
return "加簽";
}
}

6、具體怎么調(diào)用?這里我寫了個(gè)測(cè)試controller直接調(diào)用,具體如下:

/**
* @author 往事如風(fēng)
* @version 1.0
* @date 2022/9/6 17:32
* @description
*/
@RestController
@Slf4j
public class TestController {

@Resource
private HandleChainManager handleChainManager;

@PostMapping("/send")
public String duty(@RequestBody String requestBody){
String response = handleChainManager.executeHandle(BusinessConstants.REQUEST, requestBody);
return response;
}
}

7、執(zhí)行結(jié)果,會(huì)按照注解中標(biāo)記的order依次執(zhí)行。 

圖片

至此,完工。又可以開心的擼代碼了,然后在具體的執(zhí)行類中,又是一頓if-else。。。

四、源碼中運(yùn)用

4.1Mybatis源碼中的運(yùn)用

Mybatis中的緩存接口Cache,cache作為一個(gè)緩存接口,最主要的功能就是添加和獲取緩存的功能,作為接口它有11個(gè)實(shí)現(xiàn)類,分別實(shí)現(xiàn)不同的功能,下面是接口源碼和實(shí)現(xiàn)類。

package org.apache.ibatis.cache;

import java.util.concurrent.locks.ReadWriteLock;

public interface Cache {
String getId();

void putObject(Object var1, Object var2);

Object getObject(Object var1);

Object removeObject(Object var1);

void clear();

int getSize();

default ReadWriteLock getReadWriteLock(){
return null;
}
}

圖片

下面,我們來看下其中一個(gè)子類LoggingCache的源碼。主要看他的putObject方法和getObject方法,它在方法中直接傳給下一個(gè)實(shí)現(xiàn)去執(zhí)行。這個(gè)實(shí)現(xiàn)類其實(shí)是為了在獲取緩存的時(shí)候打印緩存的命中率的。

public class LoggingCache implements Cache {
private final Log log;
private final Cache delegate;
protected int requests = 0;
protected int hits = 0;

public LoggingCache(Cache delegate){
this.delegate = delegate;
this.log = LogFactory.getLog(this.getId());
}

// ...
public void putObject(Object key, Object object){
this.delegate.putObject(key, object);
}

public Object getObject(Object key){
++this.requests;
Object value = this.delegate.getObject(key);
if (value != null) {
++this.hits;
}

if (this.log.isDebugEnabled()) {
this.log.debug("Cache Hit Ratio [" + this.getId() + "]: " + this.getHitRatio());
}

return value;
}
// ...
}

最后,經(jīng)過Cache?接口各種實(shí)現(xiàn)類的處理,最終會(huì)到達(dá)PerpetualCache這個(gè)實(shí)現(xiàn)類。與之前的處理類不同的是,這個(gè)類中有一個(gè)map,在map中做存取,也就是說,最終緩存還是會(huì)保存在map中的。

public class PerpetualCache implements Cache {
private final String id;
private final Map<Object, Object> cache = new HashMap();

public PerpetualCache(String id){
this.id = id;
}

// ...

public void putObject(Object key, Object value){
this.cache.put(key, value);
}

public Object getObject(Object key){
return this.cache.get(key);
}
// ...

}

4.2spring源碼中的運(yùn)用

4.2.1DispatcherServlet類

DispatcherServlet 核心方法 doDispatch。HandlerExecutionChain只是維護(hù)HandlerInterceptor的集合,可以向其中注冊(cè)相應(yīng)的攔截器,本身不直接處理請(qǐng)求,將請(qǐng)求分配給責(zé)任鏈上注冊(cè)處理器執(zhí)行,降低職責(zé)鏈本身與處理邏輯之間的耦合程度。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

4.2.2HandlerExecutionChain類

這里分析的幾個(gè)方法,都是從DispatcherServlet類的doDispatch方法中請(qǐng)求的。

  • 獲取攔截器,執(zhí)行preHandle方法
boolean applyPreHandle(HttpServletRequest request, 
HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
}
return true;
}

在applyPreHandle方法中,執(zhí)行triggerAfterCompletion方法

void triggerAfterCompletion(HttpServletRequest request, 
HttpServletResponse response, Exception ex) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = this.interceptorIndex; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable var8) {
logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
}
}
}
}

獲取攔截器,執(zhí)行applyPostHandle方法

void applyPostHandle(HttpServletRequest request, 
HttpServletResponse response, ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = interceptors.length - 1; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}

五、總結(jié)

5.1 優(yōu)點(diǎn)

  • 將請(qǐng)求與處理解耦。
  • 請(qǐng)求處理者(節(jié)點(diǎn)對(duì)象)只需要關(guān)注自己感興趣的請(qǐng)求進(jìn)行處理即可,對(duì)于不感興趣的請(qǐng)求,轉(zhuǎn)發(fā)給下一個(gè)節(jié)點(diǎn)。
  • 具備鏈?zhǔn)絺鬟f處理請(qǐng)求功能,請(qǐng)求發(fā)送者無需知曉鏈路結(jié)構(gòu),只需等待請(qǐng)求處理結(jié)果。
  • 鏈路結(jié)構(gòu)靈活,可以通過改變鏈路的結(jié)構(gòu)動(dòng)態(tài)的新增或刪減責(zé)任。
  • 易于擴(kuò)展新的請(qǐng)求處理類(節(jié)點(diǎn)),符合開閉原則。

5.2 缺點(diǎn)

責(zé)任鏈太長(zhǎng)或者處理時(shí)間過長(zhǎng),會(huì)影響整體性能。

如果節(jié)點(diǎn)對(duì)象存在循環(huán)引用時(shí),會(huì)造成死循環(huán),導(dǎo)致系統(tǒng)崩潰。

六、參考源碼

?編程文檔: https://gitee.com/cicadasmile/butte-java-note

應(yīng)用倉庫: https://gitee.com/cicadasmile/butte-flyer-parent

責(zé)任編輯:武曉燕 來源: 知了一笑
相關(guān)推薦

2023-05-15 08:38:58

模板方法模式

2022-11-26 00:00:06

裝飾者模式Component

2018-06-25 09:32:44

2021-04-28 08:35:52

區(qū)塊鏈技術(shù)開發(fā)

2023-02-09 10:39:15

gRPC通信模式

2023-12-14 11:35:32

.NET泄露模式

2022-09-26 08:03:25

VMware虛擬機(jī)

2023-09-22 17:36:37

2021-01-28 22:31:33

分組密碼算法

2020-05-22 08:16:07

PONGPONXG-PON

2022-07-28 10:39:31

工業(yè)4.0供應(yīng)鏈

2018-06-07 13:17:12

契約測(cè)試單元測(cè)試API測(cè)試

2021-08-04 09:32:05

Typescript 技巧Partial

2022-08-08 08:25:21

Javajar 文件

2018-11-29 09:13:47

CPU中斷控制器

2019-02-13 14:15:59

Linux版本Fedora

2021-01-29 08:32:21

數(shù)據(jù)結(jié)構(gòu)數(shù)組

2021-02-06 08:34:49

函數(shù)memoize文檔

2023-07-06 13:56:14

微軟Skype

2020-10-15 06:56:51

MySQL排序
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)