一文搞懂Java設(shè)計(jì)模式之責(zé)任鏈模式
簡(jiǎn)述:
前端時(shí)間再看一些類庫(kù)的源碼,發(fā)現(xiàn)責(zé)任鏈模式的強(qiáng)大之處,尤其是和建造者模式的結(jié)合后強(qiáng)大的動(dòng)態(tài)可擴(kuò)展性更是牛逼的一塌糊涂。接下來(lái)趕緊了解一下吧!
我們先來(lái)了解一下什么是責(zé)任鏈模式:
職責(zé)鏈模式(Chain of Responsibility):使多個(gè)對(duì)象都有機(jī)會(huì)處理同一個(gè)請(qǐng)求,從而避免請(qǐng)求的發(fā)送者和接收者之間的耦合關(guān)系。將這些對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有一個(gè)對(duì)象處理它為止。
應(yīng)用場(chǎng)景:
為完成同一個(gè)請(qǐng)求,如果存在多個(gè)請(qǐng)求處理器以及未知請(qǐng)求處理器個(gè)數(shù)或者請(qǐng)求處理器可動(dòng)態(tài)配置的情況下,可以考慮使用責(zé)任鏈模式。如OKHttp的攔截器就是使用的責(zé)任鏈模式。
實(shí)例UML圖:

實(shí)例執(zhí)行流程圖:

實(shí)例:
1、實(shí)例場(chǎng)景
在公司內(nèi)部員工請(qǐng)假一般情況是這樣的:?jiǎn)T工在OA系統(tǒng)中提交一封請(qǐng)假郵件,該郵件會(huì)自動(dòng)轉(zhuǎn)發(fā)到你的直接上級(jí)領(lǐng)導(dǎo)郵箱里,如果你的請(qǐng)假的情況特殊的話,該郵件也會(huì)轉(zhuǎn)發(fā)到你上級(jí)的上級(jí)的郵箱,根據(jù)請(qǐng)假的情況天數(shù)多少,系統(tǒng)會(huì)自動(dòng)轉(zhuǎn)發(fā)相應(yīng)的責(zé)任人的郵箱。我們就以這樣一種場(chǎng)景為例完成一個(gè)責(zé)任鏈模式的代碼。為了更清晰的描述這種場(chǎng)景我們規(guī)定如下:
① GroupLeader(組長(zhǎng) ):他能批準(zhǔn)的假期為2天,如果請(qǐng)假天數(shù)超過(guò)2天就將請(qǐng)假郵件自動(dòng)轉(zhuǎn)發(fā)到組長(zhǎng)和經(jīng)理郵箱。
② Manager(經(jīng)理):他能批準(zhǔn)的假期為4天以內(nèi),如果請(qǐng)假天數(shù)大于4天將該郵件轉(zhuǎn)發(fā)到自動(dòng)轉(zhuǎn)發(fā)到組長(zhǎng)、經(jīng)理和部門領(lǐng)導(dǎo)的郵箱。
③ DepartmentHeader(部門領(lǐng)導(dǎo)):他能批準(zhǔn)的假期為7天以內(nèi),如果大于7天就只批準(zhǔn)7天。
2、實(shí)例代碼
我們清楚了上面的場(chǎng)景以后就開始定義模型:
①根據(jù)面向?qū)ο蟮乃枷胛覀兊枚x需要用到的對(duì)象。OK,為了更加清楚的說(shuō)明“責(zé)任鏈模式的可擴(kuò)展性”問(wèn)題我這里采用了建造者模式構(gòu)造Request對(duì)象,“請(qǐng)假”對(duì)象Request如下:
/**
* 類描述:請(qǐng)假對(duì)象
*
* @author lzy
*/
public class Request {
private String name;
private String reason;
private int days;
private String groupLeaderInfo;
private String managerInfo;
private String departmentHeaderInfo;
private String customInfo;
public Request(Builder builder) {
super();
this.name = builder.name;
this.reason = builder.reason;
this.days = builder.days;
this.groupLeaderInfo = builder.groupLeaderInfo;
this.managerInfo = builder.managerInfo;
this.departmentHeaderInfo = builder.departmentHeaderInfo;
this.customInfo = builder.customInfo;
}
public static class Builder {
public String name;
public String reason;
public int days;
public String groupLeaderInfo;
public String managerInfo;
public String departmentHeaderInfo;
public String customInfo;
public Builder() {
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setReason(String reason) {
this.reason = reason;
return this;
}
public Builder setDays(int days) {
this.days = days;
return this;
}
public Builder setGroupLeaderInfo(String groupLeaderInfo) {
this.groupLeaderInfo = groupLeaderInfo;
return this;
}
public Builder setManagerInfo(String managerInfo) {
this.managerInfo = managerInfo;
return this;
}
public Builder setDepartmentHeaderInfo(String departmentHeaderInfo) {
this.departmentHeaderInfo = departmentHeaderInfo;
return this;
}
public Builder setCustomInfo(String customInfo) {
this.customInfo = customInfo;
return this;
}
public Builder newRequest(Request request) {
this.name = request.name;
this.days = request.days;
this.reason = request.reason;
if (request.groupLeaderInfo != null
&& !request.groupLeaderInfo.equals("")) {
this.groupLeaderInfo = request.groupLeaderInfo;
}
if (request.managerInfo != null && !request.managerInfo.equals("")) {
this.managerInfo = request.managerInfo;
}
if (request.departmentHeaderInfo != null
&& !request.departmentHeaderInfo.equals("")) {
this.departmentHeaderInfo = request.departmentHeaderInfo;
}
if (request.customInfo != null && !request.customInfo.equals("")) {
this.customInfo = request.customInfo;
}
return this;
}
public Request build() {
return new Request(this);
}
}
public String name() {
return name;
}
public String reason() {
return reason;
}
public int days() {
return days;
}
public String groupLeaderInfo() {
return groupLeaderInfo;
}
public String managerInfo() {
return managerInfo;
}
public String departmentHeaderInfo() {
return departmentHeaderInfo;
}
public String customInfo() {
return customInfo;
}
@Override
public String toString() {
return "Request [name=" + name + ", reason=" + reason + ", days="
+ days + ",customInfo=" + customInfo + ", groupLeaderInfo="
+ groupLeaderInfo + ", managerInfo=" + managerInfo
+ ", departmentHeaderInfo=" + departmentHeaderInfo + "]";
}
}
接下來(lái)再定義“批準(zhǔn)結(jié)果”對(duì)象Result:
/**
* 類描述:結(jié)果對(duì)象
*
* @author lzy
*
*/
public class Result {
public boolean isRatify;
public String info;
public Result() {
}
public Result(boolean isRatify, String info) {
super();
this.isRatify = isRatify;
this.info = info;
}
public boolean isRatify() {
return isRatify;
}
public void setRatify(boolean isRatify) {
this.isRatify = isRatify;
}
public String getReason() {
return info;
}
public void setReason(String info) {
this.info = info;
}
@Override
public String toString() {
return "Result [isRatify=" + isRatify + ", info=" + info + "]";
}
}
②我們接下來(lái)再來(lái)定義一個(gè)接口,這個(gè)接口用于處理Request和獲取請(qǐng)求結(jié)果Result。
/**
* 接口描述:處理請(qǐng)求
*
* @author lzy
*
*/
public interface Ratify {
// 處理請(qǐng)求
public Result deal(Chain chain);
/**
* 接口描述:對(duì)request和Result封裝,用來(lái)轉(zhuǎn)發(fā)
*/
interface Chain {
// 獲取當(dāng)前request
Request request();
// 轉(zhuǎn)發(fā)request
Result proceed(Request request);
}
}
看到上面的接口,可能會(huì)有人迷惑:在接口Ratify中為什么又定義一個(gè)Chain接口呢?其實(shí)這個(gè)接口是單獨(dú)定義還是內(nèi)部接口沒(méi)有太大關(guān)系,但是考慮到Chain接口與Ratify接口的關(guān)系為提高內(nèi)聚性就定義為內(nèi)部接口了。定義Ratify接口是為了處理Request那為什么還要定義Chain接口呢?這正是責(zé)任鏈接口的精髓之處:轉(zhuǎn)發(fā)功能及可動(dòng)態(tài)擴(kuò)展“責(zé)任人”,這個(gè)接口中定義了兩個(gè)方法一個(gè)是request()就是為了獲取request,如果當(dāng)前Ratify的實(shí)現(xiàn)類獲取到request之后發(fā)現(xiàn)自己不能處理或者說(shuō)自己只能處理部分請(qǐng)求,那么他將自己的那部分能處理的就處理掉,然后重新構(gòu)建一個(gè)或者直接轉(zhuǎn)發(fā)Request給下一個(gè)責(zé)任人??赡苓@點(diǎn)說(shuō)的不容易理解,我舉個(gè)例子,在Android與后臺(tái)交互中如果使用了Http協(xié)議,當(dāng)然我們可能使用各種Http框架如HttpClient、OKHttp等,我們只需要發(fā)送要請(qǐng)求的參數(shù)就直接等待結(jié)果了,這個(gè)過(guò)程中你可能并沒(méi)有構(gòu)建請(qǐng)求頭,那么框架幫你把這部分工作給做了,它做的工程中如果使用了責(zé)任鏈模式的話,它肯定會(huì)將Request進(jìn)行包裝(也就是添加請(qǐng)求頭)成新的Request,我們姑且加他為Request1,如果你又希望Http做本地緩存,那么Request1又會(huì)被轉(zhuǎn)發(fā)到并且重新進(jìn)一步包裝為Request2。總之Chain這個(gè)接口就是起到對(duì)Request進(jìn)行重新包裝的并將包裝后的Request進(jìn)行下一步轉(zhuǎn)發(fā)的作用。如果還不是很明白也沒(méi)關(guān)系,本實(shí)例會(huì)演示這一功能機(jī)制。
③上面說(shuō)Chain是用來(lái)對(duì)Request重新包裝以及將包裝后的Request進(jìn)行下一步轉(zhuǎn)發(fā)用的,那我們就具體實(shí)現(xiàn)一下:
/**
* 類描述:實(shí)現(xiàn)Chain的真正的包裝Request和轉(zhuǎn)發(fā)功能
*
* @author lzy
*
*/
public class RealChain implements Chain {
public Request request;
public List<Ratify> ratifyList;
public int index;
/**
* 構(gòu)造方法
*
* @param ratifyList
* Ratify接口的實(shí)現(xiàn)類集合
* @param request
* 具體的請(qǐng)求Request實(shí)例
* @param index
* 已經(jīng)處理過(guò)該request的責(zé)任人數(shù)量
*/
public RealChain(List<Ratify> ratifyList, Request request, int index) {
this.ratifyList = ratifyList;
this.request = request;
this.index = index;
}
/**
* 方法描述:具體轉(zhuǎn)發(fā)功能
*/
@Override
public Result proceed(Request request) {
Result proceed = null;
if (ratifyList.size() > index) {
RealChain realChain = new RealChain(ratifyList, request, index + 1);
Ratify ratify = ratifyList.get(index);
proceed = ratify.deal(realChain);
}
return proceed;
}
/**
* 方法描述:返回當(dāng)前Request對(duì)象或者返回當(dāng)前進(jìn)行包裝后的Request對(duì)象
*/
@Override
public Request request() {
return request;
}
}
④ 經(jīng)過(guò)上面幾步我們已經(jīng)完成了責(zé)任鏈模式的核心功能,接下來(lái)我們定義幾個(gè)相關(guān)責(zé)任對(duì)象:GroupLeader、Manager和DepartmentHeader,并讓他們實(shí)現(xiàn)Ratify接口。
/**
* 組長(zhǎng)
*
* @author lzy
*
*/
public class GroupLeader implements Ratify {
@Override
public Result deal(Chain chain) {
Request request = chain.request();
System.out.println("GroupLeader=====>request:" + request.toString());
if (request.days() > 1) {
// 包裝新的Request對(duì)象
Request newRequest = new Request.Builder().newRequest(request)
.setManagerInfo(request.name() + "平時(shí)表現(xiàn)不錯(cuò),而且現(xiàn)在項(xiàng)目也不忙")
.build();
return chain.proceed(newRequest);
}
return new Result(true, "GroupLeader:早去早回");
}
}
/**
* 經(jīng)理
*
* @author lzy
*
*/
public class Manager implements Ratify {
@Override
public Result deal(Chain chain) {
Request request = chain.request();
System.out.println("Manager=====>request:" + request.toString());
if (request.days() > 3) {
// 構(gòu)建新的Request
Request newRequest = new Request.Builder().newRequest(request)
.setManagerInfo(request.name() + "每月的KPI考核還不錯(cuò),可以批準(zhǔn)")
.build();
return chain.proceed(newRequest);
}
return new Result(true, "Manager:早點(diǎn)把事情辦完,項(xiàng)目離不開你");
}
}
/**
* 部門領(lǐng)導(dǎo)
*
* @author lzy
*
*/
public class DepartmentHeader implements Ratify {
@Override
public Result deal(Chain chain) {
Request request = chain.request();
System.out.println("DepartmentHeader=====>request:"
+ request.toString());
if (request.days() > 7) {
return new Result(false, "你這個(gè)完全沒(méi)必要");
}
return new Result(true, "DepartmentHeader:不要著急,把事情處理完再回來(lái)!");
}
}
到此,責(zé)任鏈模式的一個(gè)Demo就算是完成了,但為了方便調(diào)用,我們?cè)趯懸粋€(gè)該責(zé)任鏈模式的客戶端工具類ChainOfResponsibilityClient 如下:
/**
* 類描述:責(zé)任鏈模模式工具類
*
* @author lzy
*
*/
public class ChainOfResponsibilityClient {
private ArrayList<Ratify> ratifies;
public ChainOfResponsibilityClient() {
ratifies = new ArrayList<Ratify>();
}
/**
* 方法描述:為了展示“責(zé)任鏈模式”的真正的迷人之處(可擴(kuò)展性),在這里構(gòu)造該方法以便添加自定義的“責(zé)任人”
*
* @param ratify
*/
public void addRatifys(Ratify ratify) {
ratifies.add(ratify);
}
/**
* 方法描述:執(zhí)行請(qǐng)求
*
* @param request
* @return
*/
public Result execute(Request request) {
ArrayList<Ratify> arrayList = new ArrayList<Ratify>();
arrayList.addAll(ratifies);
arrayList.add(new GroupLeader());
arrayList.add(new Manager());
arrayList.add(new DepartmentHeader());
RealChain realChain = new RealChain(this, arrayList, request, 0);
return realChain.proceed(request);
}
}
OK,我們測(cè)試一下見證奇跡吧:
/**
* 類描述:責(zé)任鏈模式測(cè)試類
*
* @author lzy
*
*/
public class Main {
public static void main(String[] args) {
Request request = new Request.Builder().setName("張三").setDays(5)
.setReason("事假").build();
ChainOfResponsibilityClient client = new ChainOfResponsibilityClient();
Result result = client.execute(request);
System.out.println("結(jié)果:" + result.toString());
}
}
這個(gè)請(qǐng)求是張三請(qǐng)事假5天,按照我們的約定應(yīng)該請(qǐng)求會(huì)到達(dá)部門領(lǐng)導(dǎo)手里,且他看到請(qǐng)求的樣式為:“ [name=張三, reason=事假, days=5customInfo=null, groupLeaderInfo=張三平時(shí)表現(xiàn)不錯(cuò),而且現(xiàn)在項(xiàng)目也不忙, managerInfo=張三每月的KPI考核還不錯(cuò),可以批準(zhǔn), departmentHeaderInfo=null]”
我們看一下打印的日志:
GroupLeader=====>request:Request [name=張三, reason=事假, days=5customInfo=null, groupLeaderInfo=null, managerInfo=null, departmentHeaderInfo=null]
Manager=====>request:Request [name=張三, reason=事假, days=5customInfo=null, groupLeaderInfo=張三平時(shí)表現(xiàn)不錯(cuò),而且現(xiàn)在項(xiàng)目也不忙, managerInfo=null, departmentHeaderInfo=null]
DepartmentHeader=====>request:Request [name=張三, reason=事假, days=5customInfo=null, groupLeaderInfo=張三平時(shí)表現(xiàn)不錯(cuò),而且現(xiàn)在項(xiàng)目也不忙, managerInfo=張三每月的KPI考核還不錯(cuò),可以批準(zhǔn), departmentHeaderInfo=null]
結(jié)果:Result [isRatify=true, info=DepartmentHeader:不要著急,把事情處理完再回來(lái)!]
OK,和預(yù)期一樣完美。剛開始就提到這個(gè)責(zé)任鏈模式是可以“動(dòng)態(tài)擴(kuò)展的”,我們驗(yàn)證一下,首先自定義一個(gè)“責(zé)任人”(其實(shí)也可以叫攔截器):
/**
* 類描述:自定義“責(zé)任人”
*
* @author lzy
*
*/
public class CustomInterceptor implements Ratify {
@Override
public Result deal(Chain chain) {
Request request = chain.request();
System.out.println("CustomInterceptor=>" + request.toString());
String reason = request.reason();
if (reason != null && reason.equals("事假")) {
Request newRequest = new Request.Builder().newRequest(request)
.setCustomInfo(request.name() + "請(qǐng)的是事假,而且很著急,請(qǐng)領(lǐng)導(dǎo)重視一下")
.build();
System.out.println("CustomInterceptor=>轉(zhuǎn)發(fā)請(qǐng)求");
return chain.proceed(newRequest);
}
return new Result(true, "同意請(qǐng)假");
}
}
然后在測(cè)試類Main.java中調(diào)用如下:
/**
* 類描述:責(zé)任鏈模式測(cè)試類
*
* @author lzy
*
*/
public class Main {
public static void main(String[] args) {
Request request = new Request.Builder().setName("張三").setDays(5)
.setReason("事假").build();
ChainOfResponsibilityClient client = new ChainOfResponsibilityClient();
client.addRatifys(new CustomInterceptor());
Result result = client.execute(request);
System.out.println("結(jié)果:" + result.toString());
}
}
OK,看一下日志:
哈哈,責(zé)任鏈模式功能之強(qiáng)大還要多用多體會(huì)呀!
到此這篇關(guān)于Java設(shè)計(jì)模式之責(zé)任鏈模式的文章就介紹到這了,更多相關(guān)Java設(shè)計(jì)模式之責(zé)任鏈模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringMVC項(xiàng)目異常處理機(jī)制詳解
SpringMVC是一種基于Java,實(shí)現(xiàn)了Web MVC設(shè)計(jì)模式,請(qǐng)求驅(qū)動(dòng)類型的輕量級(jí)Web框架,即使用了MVC架構(gòu)模式的思想,將Web層進(jìn)行職責(zé)解耦。基于請(qǐng)求驅(qū)動(dòng)指的就是使用請(qǐng)求-響應(yīng)模型,框架的目的就是幫助我們簡(jiǎn)化開發(fā),SpringMVC也是要簡(jiǎn)化我們?nèi)粘eb開發(fā)2022-08-08
JAVA實(shí)現(xiàn)簡(jiǎn)單搶紅包算法(模擬真實(shí)搶紅包)
這篇文章主要介紹了JAVA實(shí)現(xiàn)簡(jiǎn)單搶紅包算法(模擬真實(shí)搶紅包)的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-12-12
SpringMVC中@RequestMapping注解的實(shí)現(xiàn)
RequestMapping是一個(gè)用來(lái)處理請(qǐng)求地址映射的注解,本文主要介紹了SpringMVC中@RequestMapping注解的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01
JPA @Query時(shí),無(wú)法使用limit函數(shù)的問(wèn)題及解決
這篇文章主要介紹了JPA @Query時(shí),無(wú)法使用limit函數(shù)的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
RocketMQ實(shí)現(xiàn)消息分發(fā)的步驟
RocketMQ 實(shí)現(xiàn)消息分發(fā)的核心機(jī)制是通過(guò) Topic、Queue 和 Consumer Group 的配合實(shí)現(xiàn)的,下面給大家介紹RocketMQ實(shí)現(xiàn)消息分發(fā)的步驟,感興趣的朋友一起看看吧2024-03-03
基于mybatis batch實(shí)現(xiàn)批量提交大量數(shù)據(jù)
這篇文章主要介紹了基于mybatis batch實(shí)現(xiàn)批量提交大量數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05
MySQL如何設(shè)置自動(dòng)增長(zhǎng)序列SEQUENCE的方法
本文主要介紹了MySQL如何設(shè)置自動(dòng)增長(zhǎng)序列SEQUENCE的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
Spring?Cloud?Alibaba實(shí)現(xiàn)服務(wù)的無(wú)損下線功能(案例講解)
這篇文章主要介紹了Spring?Cloud?Alibaba實(shí)現(xiàn)服務(wù)的無(wú)損下線功能?,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03

