Java職責(zé)鏈模式的深入了解
一、職責(zé)鏈模式的定義與特點(diǎn)
定義:
為了避免請(qǐng)求發(fā)送者與多個(gè)請(qǐng)求處理者耦合在一起,于是將所有請(qǐng)求的處理者通過(guò)前一對(duì)象記住其下一個(gè)對(duì)象的引用而連成一條鏈;當(dāng)有請(qǐng)求發(fā)生時(shí),可將請(qǐng)求沿著這條鏈傳遞,直到有對(duì)象處理它為止。
比如我們的審批制度,低等級(jí)的審批不了的,交給上一級(jí)審批,依次類推,直到審批結(jié)束。
在責(zé)任鏈模式中,客戶只需要將請(qǐng)求發(fā)送到責(zé)任鏈上即可,無(wú)須關(guān)心請(qǐng)求的處理細(xì)節(jié)和請(qǐng)求的傳遞過(guò)程,請(qǐng)求會(huì)自動(dòng)進(jìn)行傳遞。所以責(zé)任鏈將請(qǐng)求的發(fā)送者和請(qǐng)求的處理者解耦了。
特點(diǎn):
1. 降低了對(duì)象之間的耦合度。該模式使得一個(gè)對(duì)象無(wú)須知道到底是哪一個(gè)對(duì)象處理其請(qǐng)求以及鏈的結(jié)構(gòu),發(fā)送者和接收者也無(wú)須擁有對(duì)方的明確信息。
2. 增強(qiáng)了系統(tǒng)的可擴(kuò)展性。可以根據(jù)需要增加新的請(qǐng)求處理類,滿足開閉原則。
3. 增強(qiáng)了給對(duì)象指派職責(zé)的靈活性。當(dāng)工作流程發(fā)生變化,可以動(dòng)態(tài)地改變鏈內(nèi)的成員或者調(diào)動(dòng)它們的次序,也可動(dòng)態(tài)地新增或者刪除責(zé)任。
4. 責(zé)任鏈簡(jiǎn)化了對(duì)象之間的連接。每個(gè)對(duì)象只需保持一個(gè)指向其后繼者的引用,不需保持其他所有處理者的引用,這避免了使用眾多的 if 或者 if···else 語(yǔ)句。
5. 責(zé)任分擔(dān)。每個(gè)類只需要處理自己該處理的工作,不該處理的傳遞給下一個(gè)對(duì)象完成,明確各類的責(zé)任范圍,符合類的單一職責(zé)原則。
缺點(diǎn):
1. 不能保證每個(gè)請(qǐng)求一定被處理。由于一個(gè)請(qǐng)求沒有明確的接收者,所以不能保證它一定會(huì)被處理,該請(qǐng)求可能一直傳到鏈的末端都得不到處理。
2. 對(duì)比較長(zhǎng)的職責(zé)鏈,請(qǐng)求的處理可能涉及多個(gè)處理對(duì)象,系統(tǒng)性能將受到一定影響。 3. 職責(zé)鏈建立的合理性要靠客戶端來(lái)保證,增加了客戶端的復(fù)雜性,可能會(huì)由于職責(zé)鏈的錯(cuò)誤設(shè)置而導(dǎo)致系統(tǒng)出錯(cuò),如可能會(huì)造成循環(huán)調(diào)用。
二、職責(zé)鏈模式的結(jié)構(gòu)
職責(zé)鏈模式的主要角色
抽象處理者(Handler)角色:定義一個(gè)處理請(qǐng)求的接口,包含抽象處理方法和一個(gè)后繼連接。
具體處理者(Concrete Handler)角色:實(shí)現(xiàn)抽象處理者的處理方法,判斷能否處理本次請(qǐng)求,如果可以處理請(qǐng)求則處理,否則將該請(qǐng)求轉(zhuǎn)給它的后繼者。
客戶類(Client)角色:創(chuàng)建處理鏈,并向鏈頭的具體處理者對(duì)象提交請(qǐng)求,它不關(guān)心處理細(xì)節(jié)和請(qǐng)求的傳遞過(guò)程。
責(zé)任鏈模式的本質(zhì)是解耦請(qǐng)求與處理,讓請(qǐng)求在處理鏈中能進(jìn)行傳遞與被處理;理解責(zé)任鏈模式應(yīng)當(dāng)理解其模式,而不是其具體實(shí)現(xiàn)。責(zé)任鏈模式的獨(dú)到之處是將其節(jié)點(diǎn)處理者組合成了鏈?zhǔn)浇Y(jié)構(gòu),并允許節(jié)點(diǎn)自身決定是否進(jìn)行請(qǐng)求處理或轉(zhuǎn)發(fā),相當(dāng)于讓請(qǐng)求流動(dòng)起來(lái)。
三、職責(zé)鏈模式案例
案例需求:編寫程序完成學(xué)習(xí)采購(gòu)項(xiàng)目審批系統(tǒng)
采購(gòu)員采購(gòu)教學(xué)器材,如果金額小于5000,由教學(xué)主任審批,
如果金額小于10000,由院長(zhǎng)審批
如果金額小于30000,又副校長(zhǎng)審批
如果金額大于30000,由校長(zhǎng)審批
采用職責(zé)鏈模式
那么該案例我們傳統(tǒng)的方法大致就是采用分支語(yǔ)句去解決,但是這個(gè)會(huì)導(dǎo)致我們又違反開閉原則,就是如果我們修改審批人的話會(huì)去修改類中內(nèi)容,所以我們采取職責(zé)鏈模式,將審批人類和處理類分開,解耦,分別去實(shí)現(xiàn)他。這樣的話我們想要加審批人只需要添加新類即可
UML類圖
請(qǐng)求審批類
package com.chainOfResponsibilityPattern.SubmitAccount; /** * @author wang * @version 1.0 * @packageName com.chainOfResponsibilityPattern.SubmitAccount * @className PurchaseRequest * @date 2021/12/28 19:31 * @Descriptio 該類為請(qǐng)求對(duì)象,封裝了請(qǐng)求處理的相關(guān)信息 * 變量分別為請(qǐng)求類型,編號(hào),價(jià)格 */ public class PurchaseRequest { private String type; private int id; private float price; public PurchaseRequest(String type, int id, float price) { this.type = type; this.id = id; this.price = price; } public String getType() { return type; } public int getId() { return id; } public float getPrice() { return price; } }
抽象處理類:
package com.chainOfResponsibilityPattern.SubmitAccount; /** * @author wang * @version 1.0 * @packageName com.chainOfResponsibilityPattern.SubmitAccount * @className ApprovePeople * @date 2021/12/28 19:36 * @Description 處理審批人的類,抽象處理請(qǐng)求的類 */ public abstract class ApprovePeople { /** * 下一個(gè)審批人 */ ApprovePeople approvePeople; /** * 審批人名稱 */ String name; public ApprovePeople(String name) { this.name = name; } /** * @param approvePeople * @Date 2021/12/28 19:39 * @Param * @Return void * @MetodName setNext * @Author wang * @Description 設(shè)置下一個(gè)審批人的對(duì)象 */ public void setNext(ApprovePeople approvePeople) { this.approvePeople = approvePeople; } /** * @param purchaseRequest * @Date 2021/12/28 19:40 * @Param * @Return void * @MetodName handleRequest * @Author wang * @Description 處理請(qǐng)求的方法,由該類的子類根據(jù)自己的情況去實(shí)現(xiàn) */ public abstract void handleRequest(PurchaseRequest purchaseRequest); }
教學(xué)主任類:
package com.chainOfResponsibilityPattern.SubmitAccount; /** * @author wang * @version 1.0 * @packageName com.chainOfResponsibilityPattern.SubmitAccount * @className TeacherDirector * @date 2021/12/28 19:47 * @Description 教學(xué)主任類,具體的處理請(qǐng)求的類 */ public class TeacherDirector extends ApprovePeople { public TeacherDirector(String name) { super(name); } @Override public void handleRequest(PurchaseRequest purchaseRequest) { if (purchaseRequest.getPrice() <= 5000) { System.out.println("請(qǐng)求編號(hào)為:" + purchaseRequest.getId() + "\n請(qǐng)求類型為:" + purchaseRequest.getType() + "\n請(qǐng)求金額為:" + purchaseRequest.getPrice() + "的項(xiàng)目被" + this.name + "處理成功"); } else { approvePeople.handleRequest(purchaseRequest); } } }
院長(zhǎng)類
package com.chainOfResponsibilityPattern.SubmitAccount;/** * @author wang * @version 1.0 * @packageName com.chainOfResponsibilityPattern.SubmitAccount * @className DeanApprove * @date 2021/12/28 19:52 * @Description 院長(zhǎng)處理類,具體的處理請(qǐng)求的類 */public class DeanApprove extends ApprovePeople{ public DeanApprove(String name) { super(name); } @Override public void handleRequest(PurchaseRequest purchaseRequest) { if(purchaseRequest.getPrice()> 5000 && purchaseRequest.getPrice() <= 10000) { System.out.println("請(qǐng)求編號(hào)為:" + purchaseRequest.getId() + "\n請(qǐng)求類型為:" + purchaseRequest.getType() + "\n請(qǐng)求金額為:" + purchaseRequest.getPrice() + "的項(xiàng)目被" +this.name +"處理成功"); }else { approvePeople.handleRequest(purchaseRequest); } }}
校長(zhǎng)和副校長(zhǎng)類類似與上,只需改動(dòng)處理?xiàng)l件即可
客戶端測(cè)試類;
package com.chainOfResponsibilityPattern.SubmitAccount; /** * @author wang * @version 1.0 * @packageName com.chainOfResponsibilityPattern.SubmitAccount * @className ClientTest * @date 2021/12/28 19:58 * @Description 客戶測(cè)試類 */ public class ClientTest { public static void main(String[] args) { //創(chuàng)建一個(gè)請(qǐng)求 PurchaseRequest purchaseRequest = new PurchaseRequest("體育用品", 1, 4000); //創(chuàng)建相關(guān)審批人 TeacherDirector zhang1 = new TeacherDirector("張主任"); DeanApprove li2 = new DeanApprove("李院長(zhǎng)"); VicePresident chen3 = new VicePresident("陳副院長(zhǎng)"); President liu4 = new President("劉校長(zhǎng)"); /** * 切記一定要讓個(gè)處理者之間連接起來(lái),否則會(huì)報(bào)出空指針異常,且需要構(gòu)成一個(gè)環(huán) * */ zhang1.setNext(li2); li2.setNext(chen3); chen3.setNext(liu4); liu4.setNext(zhang1); //處理請(qǐng)求 zhang1.handleRequest(purchaseRequest); } }
輸出結(jié)果
請(qǐng)求編號(hào)為:1
請(qǐng)求類型為:體育用品
請(qǐng)求金額為:4000.0的項(xiàng)目被張主任處理成功
請(qǐng)求編號(hào)為:2
請(qǐng)求類型為:修仙用品
請(qǐng)求金額為:300000.0的項(xiàng)目被劉校長(zhǎng)處理成功
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!v
相關(guān)文章
Java高級(jí)用法中的JNA類型映射注意細(xì)節(jié)及使用問題
本文介紹了在使用JNA方法映射中應(yīng)該注意的一些細(xì)節(jié)和具體的使用問題,對(duì)java??JNA類型映射注意細(xì)節(jié)感興趣的朋友一起看看吧2022-04-04Spring?Boot?中的?@HystrixCommand?注解原理及使用方法
通過(guò)使用 @HystrixCommand 注解,我們可以輕松地實(shí)現(xiàn)對(duì)方法的隔離和監(jiān)控,從而提高系統(tǒng)的可靠性和穩(wěn)定性,本文介紹了Spring Boot 中的@HystrixCommand注解是什么,其原理以及如何使用,感興趣的朋友跟隨小編一起看看吧2023-07-07關(guān)于JAVA經(jīng)典算法40題(超實(shí)用版)
本篇文章小編為大家介紹一下,關(guān)于JAVA經(jīng)典算法40題(超實(shí)用版),有需要的朋友可以參考一下2013-04-04Java使用TCP實(shí)現(xiàn)數(shù)據(jù)傳輸實(shí)例詳解
這篇文章主要介紹了Java使用TCP實(shí)現(xiàn)數(shù)據(jù)傳輸實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06Eclipse 2020-06 漢化包安裝步驟詳解(附漢化包+安裝教程)
這篇文章主要介紹了Eclipse 2020-06 漢化包安裝步驟(附漢化包+安裝教程),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08Java利用Swagger2自動(dòng)生成對(duì)外接口的文檔
這篇文章主要介紹了Java利用Swagger2自動(dòng)生成對(duì)外接口的文檔,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06Java的web開發(fā)中SSH框架的協(xié)作處理應(yīng)用筆記
這篇文章主要介紹了Java的web開發(fā)中SSH框架的協(xié)作處理應(yīng)用筆記,SSH是指Struts和Spring以及Hibernate的框架搭配,需要的朋友可以參考下2015-12-12