這篇文章主要講解了“go如何實(shí)現(xiàn)職責(zé)鏈模式”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“go如何實(shí)現(xiàn)職責(zé)鏈模式”吧!
創(chuàng)新互聯(lián)公司是一家專業(yè)提供奉化企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站制作、成都網(wǎng)站建設(shè)、H5建站、小程序制作等業(yè)務(wù)。10年已為奉化眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站制作公司優(yōu)惠進(jìn)行中。
職責(zé)鏈——英文名 Chain of responsibility 有時(shí)也被翻譯成責(zé)任鏈模式。我看網(wǎng)上叫責(zé)任鏈的更多一些,這里大家知道它們是一個(gè)東西就行了。
它是一種行為型設(shè)計(jì)模式。使用這個(gè)模式,我們能為請求創(chuàng)建一條由多個(gè)處理器組成的鏈路,每個(gè)處理器各自負(fù)責(zé)自己的職責(zé),相互之間沒有耦合,完成自己任務(wù)后請求對象即傳遞到鏈路的下一個(gè)處理器進(jìn)行處理。
職責(zé)鏈在很多流行框架里都有被用到,像中間件、攔截器等框架組件都是應(yīng)用的這種設(shè)計(jì)模式,這兩個(gè)組價(jià)大家應(yīng)該用的比較多。在做Web 接口開發(fā)的時(shí)候,像記錄訪問日志、解析Token、格式化接口響應(yīng)的統(tǒng)一結(jié)構(gòu)這些類似的項(xiàng)目公共操都是在中間件、攔截器里完成的,這樣就能讓這些基礎(chǔ)操作與接口的業(yè)務(wù)邏輯進(jìn)行解耦。
中間件、攔截器這些組件都是框架給我們設(shè)計(jì)好的直接往里面套就可以,今天我們的文章里要講的是,怎么把職責(zé)鏈應(yīng)用到我們核心的業(yè)務(wù)流程設(shè)計(jì)中,而不僅僅是只做那些基礎(chǔ)的公共操作。
上面我們說了職責(zé)鏈在項(xiàng)目公共組件中的一些應(yīng)用,讓我們能在核心邏輯的前置和后置流程中增加一些基礎(chǔ)的通用功能。但其實(shí)在一些核心的業(yè)務(wù)中,應(yīng)用職責(zé)鏈模式能夠讓我們無痛地?cái)U(kuò)展業(yè)務(wù)流程的步驟。
比如淘寶在剛剛創(chuàng)立的時(shí)候購物生成訂單處理流程起初可能是這樣的。
職責(zé)鏈模式—購物下單—清純版
整個(gè)流程比較干凈“用戶參數(shù)校驗(yàn)–購物車數(shù)據(jù)校驗(yàn)–商品庫存校驗(yàn)–運(yùn)費(fèi)計(jì)算–扣庫存—生成訂單”,我們姑且把它稱為清純版的購物下單流程,這通常都是在產(chǎn)品從0到1的時(shí)候,流程比較清純,在線購物你能實(shí)現(xiàn)在線選品、下單、支付這些就行了。
不過大家都是互聯(lián)網(wǎng)沖浪老手了,也都是吃過見過的主,這個(gè)流程要是一直這么清純,公司的PM和運(yùn)營就可以走人了。等購物網(wǎng)站跑起來,有消費(fèi)者了之后,為了提高銷售額,一般會(huì)加一些,某些品類商品滿減的促銷手段。
運(yùn)營也不能閑著,多談點(diǎn)客戶,造個(gè)購物節(jié),到時(shí)候優(yōu)惠券安排上多吸引點(diǎn)用戶。那這樣在下訂單的流程中,就得判斷購物車?yán)锏纳唐肥欠駶M足折扣條件、用戶是否有優(yōu)惠卷,有的話進(jìn)行金額減免。相當(dāng)于我們下單的內(nèi)部流程中間加了兩個(gè)子流程。
職責(zé)鏈模式—購物下單—老練版
為了實(shí)現(xiàn)新加的邏輯,我們就得在寫好的訂單流程中最起碼加兩個(gè) if else 分支才能加上這兩個(gè)邏輯。不過最要命的是因?yàn)檎麄€(gè)流程耦合在一起,修改了以后我們就得把整個(gè)流程全測一遍。并且有了上面的經(jīng)驗(yàn)我們也應(yīng)該知道這個(gè)流程以后肯定還會(huì)擴(kuò)展,比如再給你加上社群砍一刀、拼單這些功能,以后每次在訂單生成流程中加入步驟都得修改已經(jīng)寫好的代碼,怕不怕?
有朋友可能會(huì)說,互聯(lián)網(wǎng)電商購物可能確實(shí)流程比較多樣化,每個(gè)公司的流程不一樣。我們再舉個(gè)病人去醫(yī)院看病的例子,病人看病大體上基本步驟需要有:
掛號—>診室看病—>收費(fèi)處繳費(fèi)—>藥房拿藥
但是有可能有的病人需要化驗(yàn)、拍片子等等,他們在醫(yī)院就醫(yī)的流程可能是這樣的:
掛號—>初診—>影像科拍片—>復(fù)診室—>收費(fèi)處繳費(fèi)—>藥房拿藥
所以就醫(yī)這個(gè)流程也是會(huì)根據(jù)病人情況的不同,步驟有所增加的。
那么現(xiàn)在我們可以確定:假如一個(gè)流程的步驟不固定,為了在流程中增加步驟時(shí),不必修改原有已經(jīng)開發(fā)好,經(jīng)過測試的流程,我們需要讓整個(gè)流程中的各個(gè)步驟解耦,來增加流程的擴(kuò)展性,這種時(shí)候就可以使用職責(zé)鏈模式啦,這個(gè)模式可以讓我們先設(shè)置流程鏈路中有哪些步驟,再去執(zhí)行。
如果讓我們設(shè)計(jì)責(zé)任鏈應(yīng)該怎么設(shè)計(jì)呢?應(yīng)該提供和實(shí)現(xiàn)哪些方法?怎么使用它把流程里的步驟串起來呢?這里我們用職責(zé)鏈模式把就診看病這個(gè)場景中的流程步驟實(shí)現(xiàn)一遍給大家做個(gè)演示,購物下單的流程類似,咱們下去可以自己嘗試實(shí)現(xiàn)一遍,先學(xué)會(huì)職責(zé)鏈模式的結(jié)構(gòu)做些Mock示例,掌握熟練了后面再嘗試著用它解決業(yè)務(wù)中的問題。
首先我們通過上面流程擴(kuò)展的痛點(diǎn)可以想到,流程中每個(gè)步驟都應(yīng)由一個(gè)處理對象來完成邏輯抽象、所有處理對象都應(yīng)該提供統(tǒng)一的處理自身邏輯的方法,其次還應(yīng)該維護(hù)指向下一個(gè)處理對象的引用,當(dāng)前步驟自己邏輯處理完后,就調(diào)用下一個(gè)對象的處理方法,把請求交給后面的對象進(jìn)行處理,依次遞進(jìn)直到流程結(jié)束。
總結(jié)下來,實(shí)現(xiàn)責(zé)任鏈模式的對象最起碼需要包含如下特性:
成員屬性
nextHandler
: 下一個(gè)等待被調(diào)用的對象實(shí)例
成員方法
SetNext
: 把下一個(gè)對象的實(shí)例綁定到當(dāng)前對象的nextHandler
屬性上;
Do
: 當(dāng)前對象業(yè)務(wù)邏輯入口,他是每個(gè)處理對象實(shí)現(xiàn)自己邏輯的地方;
Execute
: 負(fù)責(zé)職責(zé)鏈上請求的處理和傳遞;它會(huì)調(diào)用當(dāng)前對象的Do
,nextHandler
不為空則調(diào)用nextHandler.Do
;
如果抽象成 UML 類圖表示的話,差不多就是下面這個(gè)樣子。
定義了一個(gè)職責(zé)鏈模式處理對象的接口Handler
,由ConcreteHandler
–具體處理對象的類型來實(shí)現(xiàn)。
觀察上圖以及上面對象特性的分析,其實(shí)是能看出 SetNext
和 Execute
這兩個(gè)行為是每個(gè) ConcreteHandler
都一樣的,所以這兩個(gè)可以交給抽象處理類型來實(shí)現(xiàn),每個(gè)具體處理對象再繼承抽象類型,即可減少重復(fù)操作。
所以責(zé)任鏈模式的抽象和提煉可以進(jìn)化成下圖這樣:
了解完職責(zé)鏈模式從接口和類型設(shè)計(jì)上應(yīng)該怎么實(shí)現(xiàn)后,我們進(jìn)入代碼實(shí)現(xiàn)環(huán)節(jié),職責(zé)鏈模式如果用純面向?qū)ο蟮恼Z言實(shí)現(xiàn)起來還是很方便的,把上面的UML類圖直接翻譯成接口、抽象類,再搞幾個(gè)實(shí)現(xiàn)類就完事。
想把上面這個(gè)UML類圖翻譯成Go代碼還是有點(diǎn)難度的。這里咱們提供一個(gè)用 Go 實(shí)現(xiàn)職責(zé)鏈模式完成醫(yī)院就診流程的代碼示例。
雖然 Go 不支持繼承,不過我們還是能用類型的匿名組合來實(shí)現(xiàn),下面以病人去醫(yī)院看病這個(gè)處理流程為例提供一個(gè)具體示例。
看病的具體流程如下:
掛號—>診室看病—>收費(fèi)處繳費(fèi)—>藥房拿藥
我們的目標(biāo)是利用責(zé)任鏈模式,實(shí)現(xiàn)這個(gè)流程中的每個(gè)步驟,且相互間不耦合,還支持向流程中增加步驟。
先來實(shí)現(xiàn)職責(zé)鏈模式里的公共部分—即模式的接口和抽象類
type PatientHandler interface {
Execute(*patient) error SetNext(PatientHandler) PatientHandler Do(*patient) error}// 充當(dāng)抽象類型,實(shí)現(xiàn)公共方法,抽象方法不實(shí)現(xiàn)留給實(shí)現(xiàn)類自己實(shí)現(xiàn)type Next struct {
nextHandler PatientHandler}func (n *Next) SetNext(handler PatientHandler) PatientHandler {
n.nextHandler = handler return handler}func (n *Next) Execute(patient *patient) (err error) {
// 調(diào)用不到外部類型的 Do 方法,所以 Next 不能實(shí)現(xiàn) Do 方法
if n.nextHandler != nil {
if err = n.nextHandler.Do(patient); err != nil {
return
}
return n.nextHandler.Execute(patient)
}
return}
上面代碼中Next
類型充當(dāng)了模式中抽象類的角色,關(guān)于這個(gè)Next
類型這里再重點(diǎn)說明一下。
在我們的職責(zé)鏈的UML圖里有說明Do
方法是一個(gè)抽象方法,留給具體處理請求的類來實(shí)現(xiàn),所以這里Next
類型充當(dāng)抽象類型,只實(shí)現(xiàn)公共方法,抽象方法留給實(shí)現(xiàn)類自己實(shí)現(xiàn)。并且由于 Go 并不支持繼承,即使Next
實(shí)現(xiàn)了Do
方法,也不能達(dá)到在父類方法中調(diào)用子類方法的效果—即在我們的例子里面用Next
類型的Execute
方法調(diào)用不到外部實(shí)現(xiàn)類型的Do
方法。
所以我們這里選擇Next
類型直接不實(shí)現(xiàn)Do
方法,這也是在暗示這個(gè)類型是專門用作讓實(shí)現(xiàn)類進(jìn)行內(nèi)嵌組合使用的。
接下來我們定義職責(zé)鏈要處理的請求,再回看一下我們的UML圖,實(shí)現(xiàn)處理邏輯和請求傳遞的Do
、Execute
方法的參數(shù)都是流程中要處理的請求。這里是醫(yī)院接診的流程,所以我們定義一個(gè)患者類作為流程的請求。
//流程中的請求類--患者type patient struct {
Name string
RegistrationDone bool
DoctorCheckUpDone bool
MedicineDone bool
PaymentDone bool}
然后我們按照掛號—>診室看病—>收費(fèi)處繳費(fèi)—>藥房拿藥這個(gè)流程定義四個(gè)步驟的處理類,來分別實(shí)現(xiàn)每個(gè)環(huán)節(jié)的邏輯。
// Reception 掛號處處理器type Reception struct {
Next}func (r *Reception) Do(p *patient) (err error) {
if p.RegistrationDone {
fmt.Println("Patient registration already done")
return
}
fmt.Println("Reception registering patient")
p.RegistrationDone = true
return}// Clinic 診室處理器--用于醫(yī)生給病人看病type Clinic struct {
Next}func (d *Clinic) Do(p *patient) (err error) {
if p.DoctorCheckUpDone {
fmt.Println("Doctor checkup already done")
return
}
fmt.Println("Doctor checking patient")
p.DoctorCheckUpDone = true
return}// Cashier 收費(fèi)處處理器type Cashier struct {
Next}func (c *Cashier) Do(p *patient) (err error) {
if p.PaymentDone {
fmt.Println("Payment Done")
return
}
fmt.Println("Cashier getting money from patient patient")
p.PaymentDone = true
return}// Pharmacy 藥房處理器type Pharmacy struct {
Next}func (m *Pharmacy) Do (p *patient) (err error) {
if p.MedicineDone {
fmt.Println("Medicine already given to patient")
return
}
fmt.Println("Pharmacy giving medicine to patient")
p.MedicineDone = true
return}
處理器定義好了,怎么給用他們串成患者就診這個(gè)流程呢?
func main() {
receptionHandler := &Reception{}
patient := &patient{Name: "abc"}
// 設(shè)置病人看病的鏈路
receptionHandler.SetNext(&Clinic{}).SetNext(&Cashier{}).SetNext(&Pharmacy{})
receptionHandler.Execute(patient)}
上面的鏈?zhǔn)秸{(diào)用看起來是不是很清爽,嘿嘿別高興太早,這里邊有個(gè)BUG— 即Reception
接診掛號這個(gè)步驟提供的邏輯沒有調(diào)用到,所以我們這里再定義個(gè)StartHandler
類型,它不提供處理實(shí)現(xiàn)只是作為第一個(gè)Handler
向下轉(zhuǎn)發(fā)請求
// StartHandler 不做操作,作為第一個(gè)Handler向下轉(zhuǎn)發(fā)請求type StartHandler struct {
Next}// Do 空Handler的Dofunc (h *StartHandler) Do(c *patient) (err error) {
// 空Handler 這里什么也不做 只是載體 do nothing...
return}
這也是Go 語法限制,公共方法Exeute
并不能像面向?qū)ο竽菢酉日{(diào)用this.Do
再調(diào)用this.nextHandler.Do
具體原因咱們上邊已經(jīng)解釋過了,如果覺得不清楚的可以拿Java實(shí)現(xiàn)一遍看看區(qū)別,再琢磨一下為啥Go里邊不行。
所以整個(gè)流程每個(gè)環(huán)節(jié)都能被正確執(zhí)行到,應(yīng)該這樣把處理類串起來。
func main() {
patientHealthHandler := StartHandler{}
//
patient := &patient{Name: "abc"}
// 設(shè)置病人看病的鏈路
patientHealthHandler.SetNext(&Reception{}).// 掛號
SetNext(&Clinic{}). // 診室看病
SetNext(&Cashier{}). // 收費(fèi)處交錢
SetNext(&Pharmacy{}) // 藥房拿藥
//還可以擴(kuò)展,比如中間加入化驗(yàn)科化驗(yàn),圖像科拍片等等
// 執(zhí)行上面設(shè)置好的業(yè)務(wù)流程
if err := patientHealthHandler.Execute(patient); err != nil {
// 異常
fmt.Println("Fail | Error:" + err.Error())
return
}
// 成功
fmt.Println("Success")}
感謝各位的閱讀,以上就是“go如何實(shí)現(xiàn)職責(zé)鏈模式”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對go如何實(shí)現(xiàn)職責(zé)鏈模式這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
新聞名稱:go如何實(shí)現(xiàn)職責(zé)鏈模式
文章路徑:http://muchs.cn/article36/pioopg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、品牌網(wǎng)站建設(shè)、網(wǎng)站營銷、企業(yè)網(wǎng)站制作、網(wǎng)站排名、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)