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

你也是業(yè)務(wù)開發(fā)?提前用這個設(shè)計模式預(yù)防產(chǎn)品加需求吧

開發(fā) 項目管理
職責鏈模式所擁有的特點讓流程中的每個處理節(jié)點都只需關(guān)注滿足自己處理條件的請求進行處理即可,對于不感興趣的請求,會直接轉(zhuǎn)發(fā)給下一個節(jié)點對象進行處理。

大家好,我是每周在這里陪大家一起進步的網(wǎng)管。

今天繼續(xù)更新設(shè)計模式相關(guān)的文章,我在前面兩篇關(guān)于模板模式和策略模式的文章里給大家說過一個我總結(jié)的"暴論":“模板、策略和職責鏈三個設(shè)計模式是解決業(yè)務(wù)系統(tǒng)流程復(fù)雜多變這個痛點的利器”。這篇文章我們就來一起說說這第三個設(shè)計模式利器—職責鏈模式。

職責鏈模式

職責鏈——英文名 Chain of responsibility 有時也被翻譯成責任鏈模式。我看網(wǎng)上叫責任鏈的更多一些,這里大家知道它們是一個東西就行了。

它是一種行為型設(shè)計模式。使用這個模式,我們能為請求創(chuàng)建一條由多個處理器組成的鏈路,每個處理器各自負責自己的職責,相互之間沒有耦合,完成自己任務(wù)后請求對象即傳遞到鏈路的下一個處理器進行處理。

職責鏈在很多流行框架里都有被用到,像中間件、攔截器等框架組件都是應(yīng)用的這種設(shè)計模式,這兩個組價大家應(yīng)該用的比較多。在做Web 接口開發(fā)的時候,像記錄訪問日志、解析Token、格式化接口響應(yīng)的統(tǒng)一結(jié)構(gòu)這些類似的項目公共操都是在中間件、攔截器里完成的,這樣就能讓這些基礎(chǔ)操作與接口的業(yè)務(wù)邏輯進行解耦。

中間件、攔截器這些組件都是框架給我們設(shè)計好的直接往里面套就可以,今天我們的文章里要講的是,怎么把職責鏈應(yīng)用到我們核心的業(yè)務(wù)流程設(shè)計中,而不僅僅是只做那些基礎(chǔ)的公共操作。

職責鏈的價值

上面我們說了職責鏈在項目公共組件中的一些應(yīng)用,讓我們能在核心邏輯的前置和后置流程中增加一些基礎(chǔ)的通用功能。但其實在一些核心的業(yè)務(wù)中,應(yīng)用職責鏈模式能夠讓我們無痛地擴展業(yè)務(wù)流程的步驟。

比如淘寶在剛剛創(chuàng)立的時候購物生成訂單處理流程起初可能是這樣的。

圖片

職責鏈模式—購物下單—清純版

整個流程比較干凈"用戶參數(shù)校驗--購物車數(shù)據(jù)校驗--商品庫存校驗--運費計算--扣庫存—生成訂單",我們姑且把它稱為清純版的購物下單流程,這通常都是在產(chǎn)品從0到1的時候,流程比較清純,在線購物你能實現(xiàn)在線選品、下單、支付這些就行了。

不過大家都是互聯(lián)網(wǎng)沖浪老手了,也都是吃過見過的主,這個流程要是一直這么清純,公司的PM和運營就可以走人了。等購物網(wǎng)站跑起來,有消費者了之后,為了提高銷售額,一般會加一些,某些品類商品滿減的促銷手段。

運營也不能閑著,多談點客戶,造個購物節(jié),到時候優(yōu)惠券安排上多吸引點用戶。那這樣在下訂單的流程中,就得判斷購物車里的商品是否滿足折扣條件、用戶是否有優(yōu)惠卷,有的話進行金額減免。相當于我們下單的內(nèi)部流程中間加了兩個子流程。

圖片

職責鏈模式—購物下單—老練版

為了實現(xiàn)新加的邏輯,我們就得在寫好的訂單流程中最起碼加兩個 if else 分支才能加上這兩個邏輯。不過最要命的是因為整個流程耦合在一起,修改了以后我們就得把整個流程全測一遍。并且有了上面的經(jīng)驗我們也應(yīng)該知道這個流程以后肯定還會擴展,比如再給你加上社群砍一刀、拼單這些功能,以后每次在訂單生成流程中加入步驟都得修改已經(jīng)寫好的代碼,怕不怕?

有朋友可能會說,互聯(lián)網(wǎng)電商購物可能確實流程比較多樣化,每個公司的流程不一樣。我們再舉個病人去醫(yī)院看病的例子,病人看病大體上基本步驟需要有:

掛號—>診室看病—>收費處繳費—>藥房拿藥

但是有可能有的病人需要化驗、拍片子等等,他們在醫(yī)院就醫(yī)的流程可能是這樣的:

掛號—>初診—>影像科拍片—>復(fù)診室—>收費處繳費—>藥房拿藥

所以就醫(yī)這個流程也是會根據(jù)病人情況的不同,步驟有所增加的。

那么現(xiàn)在我們可以確定:假如一個流程的步驟不固定,為了在流程中增加步驟時,不必修改原有已經(jīng)開發(fā)好,經(jīng)過測試的流程,我們需要讓整個流程中的各個步驟解耦,來增加流程的擴展性,這種時候就可以使用職責鏈模式啦,這個模式可以讓我們先設(shè)置流程鏈路中有哪些步驟,再去執(zhí)行。

用職責鏈模式實現(xiàn)流程

如果讓我們設(shè)計責任鏈應(yīng)該怎么設(shè)計呢?應(yīng)該提供和實現(xiàn)哪些方法?怎么使用它把流程里的步驟串起來呢?這里我們用職責鏈模式把就診看病這個場景中的流程步驟實現(xiàn)一遍給大家做個演示,購物下單的流程類似,咱們下去可以自己嘗試實現(xiàn)一遍,先學會職責鏈模式的結(jié)構(gòu)做些Mock示例,掌握熟練了后面再嘗試著用它解決業(yè)務(wù)中的問題。

首先我們通過上面流程擴展的痛點可以想到,流程中每個步驟都應(yīng)由一個處理對象來完成邏輯抽象、所有處理對象都應(yīng)該提供統(tǒng)一的處理自身邏輯的方法,其次還應(yīng)該維護指向下一個處理對象的引用,當前步驟自己邏輯處理完后,就調(diào)用下一個對象的處理方法,把請求交給后面的對象進行處理,依次遞進直到流程結(jié)束。

總結(jié)下來,實現(xiàn)責任鏈模式的對象最起碼需要包含如下特性:

  • 成員屬性

nextHandler: 下一個等待被調(diào)用的對象實例

  • 成員方法

SetNext?: 把下一個對象的實例綁定到當前對象的nextHandler屬性上;

Do: 當前對象業(yè)務(wù)邏輯入口,他是每個處理對象實現(xiàn)自己邏輯的地方;

Execute?: 負責職責鏈上請求的處理和傳遞;它會調(diào)用當前對象的Do,nextHandler?不為空則調(diào)用nextHandler.Do;

如果抽象成 UML 類圖表示的話,差不多就是下面這個樣子。

圖片

定義了一個職責鏈模式處理對象的接口?Handler?,由ConcreteHandler --具體處理對象的類型來實現(xiàn)。

觀察上圖以及上面對象特性的分析,其實是能看出 SetNext? 和 Execute? 這兩個行為是每個 ConcreteHandler 都一樣的,所以這兩個可以交給抽象處理類型來實現(xiàn),每個具體處理對象再繼承抽象類型,即可減少重復(fù)操作。

所以責任鏈模式的抽象和提煉可以進化成下圖這樣:

圖片

了解完職責鏈模式從接口和類型設(shè)計上應(yīng)該怎么實現(xiàn)后,我們進入代碼實現(xiàn)環(huán)節(jié),職責鏈模式如果用純面向?qū)ο蟮恼Z言實現(xiàn)起來還是很方便的,把上面的UML類圖直接翻譯成接口、抽象類,再搞幾個實現(xiàn)類就完事。

想把上面這個UML類圖翻譯成Go代碼還是有點難度的。這里咱們提供一個用 Go 實現(xiàn)職責鏈模式完成醫(yī)院就診流程的代碼示例。

職責鏈 Go 代碼實現(xiàn)

雖然 Go 不支持繼承,不過我們還是能用類型的匿名組合來實現(xiàn),下面以病人去醫(yī)院看病這個處理流程為例提供一個具體示例。

看病的具體流程如下:

掛號—>診室看病—>收費處繳費—>藥房拿藥

我們的目標是利用責任鏈模式,實現(xiàn)這個流程中的每個步驟,且相互間不耦合,還支持向流程中增加步驟。

先來實現(xiàn)職責鏈模式里的公共部分—即模式的接口和抽象類

"本文使用的完整可運行源碼
去公眾號「網(wǎng)管叨bi叨」發(fā)送【設(shè)計模式】即可領(lǐng)取"
type PatientHandler interface {
Execute(*patient) error
SetNext(PatientHandler) PatientHandler
Do(*patient) error
}
// 充當抽象類型,實現(xiàn)公共方法,抽象方法不實現(xiàn)留給實現(xiàn)類自己實現(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 不能實現(xiàn) Do 方法
if n.nextHandler != nil {
if err = n.nextHandler.Do(patient); err != nil {
return
}

return n.nextHandler.Execute(patient)
}

return
}

上面代碼中Next?類型充當了模式中抽象類的角色,關(guān)于這個Next類型這里再重點說明一下。

在我們的職責鏈的UML圖里有說明Do?方法是一個抽象方法,留給具體處理請求的類來實現(xiàn),所以這里Next?類型充當抽象類型,只實現(xiàn)公共方法,抽象方法留給實現(xiàn)類自己實現(xiàn)。并且由于 Go 并不支持繼承,即使Next?實現(xiàn)了Do?方法,也不能達到在父類方法中調(diào)用子類方法的效果—即在我們的例子里面用Next? 類型的Execute?方法調(diào)用不到外部實現(xiàn)類型的Do方法。

所以我們這里選擇Next?類型直接不實現(xiàn)Do方法,這也是在暗示這個類型是專門用作讓實現(xiàn)類進行內(nèi)嵌組合使用的。

接下來我們定義職責鏈要處理的請求,再回看一下我們的UML圖,實現(xiàn)處理邏輯和請求傳遞的Do、Execute方法的參數(shù)都是流程中要處理的請求。這里是醫(yī)院接診的流程,所以我們定義一個患者類作為流程的請求。

//流程中的請求類--患者
type patient struct {
Name string
RegistrationDone bool
DoctorCheckUpDone bool
MedicineDone bool
PaymentDone bool
}

然后我們按照掛號—>診室看病—>收費處繳費—>藥房拿藥這個流程定義四個步驟的處理類,來分別實現(xiàn)每個環(huán)節(jié)的邏輯。

"本文使用的完整可運行源碼
去公眾號「網(wǎng)管叨bi叨」發(fā)送【設(shè)計模式】即可領(lǐng)取"
// 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 收費處處理器
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
}

處理器定義好了,怎么給用他們串成患者就診這個流程呢?

func main() {
receptionHandler := &Reception{}
patient := &patient{Name: "abc"}
// 設(shè)置病人看病的鏈路
receptionHandler.SetNext(&Clinic{}).SetNext(&Cashier{}).SetNext(&Pharmacy{})
receptionHandler.Execute(patient)
}

上面的鏈式調(diào)用看起來是不是很清爽,嘿嘿別高興太早,這里邊有個BUG— 即Recepiton?接診掛號這個步驟提供的邏輯沒有調(diào)用到,所以我們這里再定義個StartHandler? 類型,它不提供處理實現(xiàn)只是作為第一個Handler向下轉(zhuǎn)發(fā)請求

"本文使用的完整可運行源碼
去公眾號「網(wǎng)管叨bi叨」發(fā)送【設(shè)計模式】即可領(lǐng)取"
// StartHandler 不做操作,作為第一個Handler向下轉(zhuǎn)發(fā)請求
type StartHandler struct {
Next
}

// Do 空Handler的Do
func (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實現(xiàn)一遍看看區(qū)別,再琢磨一下為啥Go里邊不行。

所以整個流程每個環(huán)節(jié)都能被正確執(zhí)行到,應(yīng)該這樣把處理類串起來。

"本文使用的完整可運行源碼
去公眾號「網(wǎng)管叨bi叨」發(fā)送【設(shè)計模式】即可領(lǐng)取"
func main() {
patientHealthHandler := StartHandler{}
//
patient := &patient{Name: "abc"}
// 設(shè)置病人看病的鏈路
patientHealthHandler.SetNext(&Reception{}).// 掛號
SetNext(&Clinic{}). // 診室看病
SetNext(&Cashier{}). // 收費處交錢
SetNext(&Pharmacy{}) // 藥房拿藥
//還可以擴展,比如中間加入化驗科化驗,圖像科拍片等等

// 執(zhí)行上面設(shè)置好的業(yè)務(wù)流程
if err := patientHealthHandler.Execute(patient); err != nil {
// 異常
fmt.Println("Fail | Error:" + err.Error())
return
}
// 成功
fmt.Println("Success")
}

本文的完整源碼,已經(jīng)同步收錄到我整理的電子教程里啦,可向我的公眾號「網(wǎng)管叨bi叨」發(fā)送關(guān)鍵字【設(shè)計模式】領(lǐng)取。

總結(jié)

職責鏈模式所擁有的特點讓流程中的每個處理節(jié)點都只需關(guān)注滿足自己處理條件的請求進行處理即可,對于不感興趣的請求,會直接轉(zhuǎn)發(fā)給下一個節(jié)點對象進行處理。

另外職責鏈也可以設(shè)置中止條件,針對我們文中的例子就是在Execute方法里加判斷,一旦滿足中止后就不再繼續(xù)往鏈路的下級節(jié)點傳遞請求。Gin 的中間件的abort方法就是按照這個原理實現(xiàn)的,同時這也是職責鏈跟裝飾器模式的一個區(qū)別,裝飾器模式無法在增強實體的過程中停止,只能執(zhí)行完整個裝飾鏈路。

后面大家可以看看針對那些可能未來經(jīng)常會變的核心業(yè)務(wù)流程,可以在設(shè)計初期就考慮使用職責鏈來實現(xiàn),減輕未來流程不停迭代時不好擴展的痛點。當然職責鏈也不是萬能的,對于那些固定的流程顯然是不適合的。咱們千萬不要手里拿著錘子就看什么都是釘子,所有的設(shè)計模式一定要用在合適的地方。

既然這里提到了裝飾器,那么下一期就寫寫裝飾器吧,不對,裝飾器算是代理模式的一個特殊應(yīng)用,那就還是先介紹代理未來再介紹裝飾器吧,這樣閱讀體驗會更好一些。

責任編輯:武曉燕 來源: 網(wǎng)管叨bi叨
相關(guān)推薦

2013-05-28 14:39:11

802.11acWiFi規(guī)劃

2024-10-14 08:39:29

工廠模式策略模式代碼

2022-10-31 09:55:42

OKR績效模版模式

2022-10-26 08:48:55

IT崗位產(chǎn)品經(jīng)理

2016-11-22 16:34:11

githubgit前端

2014-09-26 10:00:25

驅(qū)動設(shè)計DDD領(lǐng)域

2010-08-11 09:15:07

設(shè)計模式Python

2015-08-03 14:51:59

2019-08-21 09:14:37

設(shè)計師B端需求

2012-01-12 13:55:54

2021-04-30 23:32:12

云托管工具安全

2021-04-29 15:01:23

SASE云安全網(wǎng)絡(luò)安全

2022-01-25 12:41:31

ChromeResponse接口

2022-09-19 08:48:03

項目初始化線程

2024-02-01 09:44:58

無頭瀏覽器網(wǎng)絡(luò)瀏覽器GUI

2021-01-07 10:15:55

開發(fā) Java開源

2021-07-28 08:31:25

設(shè)計系統(tǒng)應(yīng)用

2009-11-18 13:20:07

PHP設(shè)計模式

2023-07-26 08:34:40

VueReact

2021-09-30 09:54:49

無人機國慶交通
點贊
收藏

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