一、問題
業務擴張與縮減過程中,對應地就會有功能的增減,如果每次功能的增減都需要改動非常多的程式碼,就會導致程式碼邏輯不夠清晰易懂,業務邏輯混亂的問題;
在實際開發中,將整個業務流程拆分不同的子流程,對應地每個程式碼塊節點處理一個子流程,最後將這些程式碼塊串起來執行,即可對應整個業務流程;
當該條業務有子流程增減時,只需要增減對應的程式碼塊節點即可,這就是應用了責任鏈模式;
二、基本介紹
責任鏈模式是一種行為設計模式,用於將請求的傳送者和接收者解耦,透過多個處理節點對請求的處理,將請求的傳遞與處理鏈條化;
在實際應用中,責任鏈模式常用於處理複雜的請求處理流程,使得請求能夠沿著處理鏈依次傳遞,每個處理節點負責判斷能否處理請求並進行處理或轉發;
每個處理節點對應一個業務子流程,當業務子流程有增減時只需要對應的增減處理節點;
三、模式結構
責任鏈模式主要包含介面、子節點實現、資料型別、客戶端四部分:
介面:定義處理請求資料的方法,通常包括具體的數據處理方法、責任鏈轉移方法和子節點連結方法;
子節點實現:實現介面定義的方法,負責處理請求資料和節點轉移;
資料型別:定義請求與返回的數據結構,各個節點從該結構中讀取要處理的資料,以及寫回處理後的資料;
客戶端:建立並組裝責任鏈,向責任鏈的起始點提交請求資料;
四、工作原理
請求提交:客戶端建立請求資料並組裝責任鏈,將請求資料提交給責任鏈的第一個處理者(即起始點);
處理判斷:每個子節點處理者根據自身的業務邏輯判斷是否能夠處理該請求;如果可以處理,則處理請求並寫回結果,遇到錯誤時可以提前返回;如果不能處理,則將請求傳遞給責任鏈中的下一個處理者;
責任鏈傳遞:請求沿著責任鏈依次傳遞,直到找到能夠處理請求的處理者為止;
請求完成:當某個處理者成功處理請求後,責任鏈模式可以選擇終止傳遞或繼續傳遞給下一個處理者。
五、例項分析
Handler 介面
Handler
是每個邏輯節點的介面,定義了三個方法,分別是:Execute(*Data) error
:接收請求資料,負責邏輯節點的執行與轉移,即從前往後依次執行責任鏈上的每個節點,直至出錯返回或是執行完畢;SetNext(Handler) Handler
:負責將某個節點連結到當前節點的後面,並返回後一個節點,保證能夠依次連結器所有的邏輯節點;Do(*Data) error
:接收請求資料,真正的邏輯處理的部分,由具體的業務邏輯節點實現,在Execute
方法中被呼叫;
中間物件 Next
Next
結構體,是一個沒有具體邏輯的抽象節點,它包含了Handler
介面的元素,用來儲存下一個邏輯節點;Next
結構體實現了Handler
介面中的Execute
方法和SetNext
方法,但是沒有實現Do
方法,所以其沒有實現Handler
介面;其
SetNext
方法用於將下一個邏輯節點儲存到 nextHandler 中,並且返回 nextHandler 的地址;其
Execute
方法會呼叫 nextHandler 的Do
方法:如果 nextHandler 為空,則說明達到末尾,直接返回;否則呼叫 nextHandler 的Do
方法,執行具體子節點的邏輯處理。如果子節點邏輯執行中失敗了,則提前返回,不再執行後續節點;執行成功了,則進行邏輯節點的轉移,繼續執行下一個節點的邏輯處理;此時
Next
結構體可以理解為一箇中間物件,其實現了Execute
和SetNext
方法,避免了在 A、B、C 中重複實現這兩個方法,子節點只需要負責具體的邏輯處理,而不需要關注責任鏈的拼接與轉移;Next
結構體沒有實現Do
方法,不能在Execute
方法中直接呼叫自己的Do
方法,只能呼叫 nextHandler 的Do
方法,以下是一個錯誤的示例;
func (n *Next) Execute(data *Data) (err error) { if n == nil { return } if err = n.Do(data); err != nil { return } return n.nextHandler.Execute(data) }
子節點
A、B、C 結構體就是處理具體邏輯的子節點,都是包含了
Next
匿名組合體的結構,其實現了Do
方法,而Next
實現了Execute
和 SetNext 方法,那麼 A、B、C 就都實現了Handler
介面,可以被當作一個子節點,被連結到責任鏈上;Do
方法從Data
中接收傳入的資料,同時也可以將結果寫回Data
,處理中出現錯誤時,也可以提前返回;StartHandler
也是一個子節點,不過不處理任何邏輯,只是作為第一個Handler
向下轉發請求;
// 責任鏈模式 package main import "fmt" type Handler interface { Execute(*Data) error // 負責邏輯節點的執行與轉移 SetNext(Handler) Handler // 負責串起各個邏輯節點 Do(*Data) error // 真正的邏輯處理的部分 } type Next struct { nextHandler Handler } func (n *Next) SetNext(handler Handler) Handler { n.nextHandler = handler return handler } func (n *Next) Execute(data *Data) (err error) { if n.nextHandler == nil { return } if err = n.nextHandler.Do(data); err != nil { return } return n.nextHandler.Execute(data) } type A struct { Next } func (m *A) Do(d *Data) (err error) { if d.IsADone { fmt.Println("A has been done") return } fmt.Println("Deal A") d.IsADone = true return } type B struct { Next } func (m *B) Do(d *Data) (err error) { if d.IsBDone { fmt.Println("B has been done") return } fmt.Println("Deal B") d.IsBDone = true return } type C struct { Next } func (m *C) Do(d *Data) (err error) { if d.IsCDone { fmt.Println("C has been done") return } fmt.Println("Deal C") d.IsCDone = true return } // StartHandler 不做操作,作為第一個 Handler 向下轉發請求 type StartHandler struct { Next } // Do 空 Handler 的 Do func (h *StartHandler) Do(d *Data) (err error) { // 空 Handler 這裏什麼也不做,只是載體 do nothing... fmt.Println("StartHandler do nothing") return } type Data struct { Name string IsADone bool IsBDone bool IsCDone bool } func main() { s := StartHandler{} s.SetNext(&A{}).SetNext(&B{}).SetNext(&C{}).SetNext(&B{}) // 執行業務流程中的各個責任點 data := &Data{Name: "abc"} if err := s.Execute(data); err != nil { fmt.Println("Error:" + err.Error()) return } fmt.Println("Success") }
六、結論
責任鏈模式透過將請求的傳送者和接收者解耦,提供了一種靈活且可擴充套件的處理方式;它能夠動態地組織和分配責任,使得系統更易於維護和擴充套件;
責任鏈模式的核心思想是「鏈式處理」,在處理複雜業務流程時特別有用,它不僅提高了系統的靈活性和可擴充套件性,同時也使得程式碼更加清晰和易於理解;
在實際應用中,責任鏈模式常用於處理請求的優先順序排序、許可權校驗、日誌記錄等場景,為複雜系統的請求處理提供了一種有效的解決方案;