切換語言為:簡體

go 實現責任鏈模式

  • 爱糖宝
  • 2024-08-27
  • 2057
  • 0
  • 0

一、問題

  • 業務擴張與縮減過程中,對應地就會有功能的增減,如果每次功能的增減都需要改動非常多的程式碼,就會導致程式碼邏輯不夠清晰易懂,業務邏輯混亂的問題;

  • 在實際開發中,將整個業務流程拆分不同的子流程,對應地每個程式碼塊節點處理一個子流程,最後將這些程式碼塊串起來執行,即可對應整個業務流程;

  • 當該條業務有子流程增減時,只需要增減對應的程式碼塊節點即可,這就是應用了責任鏈模式;

二、基本介紹

  • 責任鏈模式是一種行為設計模式,用於將請求的傳送者和接收者解耦,透過多個處理節點對請求的處理,將請求的傳遞與處理鏈條化

  • 在實際應用中,責任鏈模式常用於處理複雜的請求處理流程,使得請求能夠沿著處理鏈依次傳遞,每個處理節點負責判斷能否處理請求並進行處理或轉發;

  • 每個處理節點對應一個業務子流程,當業務子流程有增減時只需要對應的增減處理節點;

三、模式結構

  • 責任鏈模式主要包含介面、子節點實現、資料型別、客戶端四部分:

  • 介面:定義處理請求資料的方法,通常包括具體的數據處理方法、責任鏈轉移方法和子節點連結方法;

  • 子節點實現:實現介面定義的方法,負責處理請求資料和節點轉移;

  • 資料型別:定義請求與返回的數據結構,各個節點從該結構中讀取要處理的資料,以及寫回處理後的資料;

  • 客戶端:建立並組裝責任鏈,向責任鏈的起始點提交請求資料;

四、工作原理

  • 請求提交:客戶端建立請求資料並組裝責任鏈,將請求資料提交給責任鏈的第一個處理者(即起始點);

  • 處理判斷:每個子節點處理者根據自身的業務邏輯判斷是否能夠處理該請求;如果可以處理,則處理請求並寫回結果,遇到錯誤時可以提前返回;如果不能處理,則將請求傳遞給責任鏈中的下一個處理者;

  • 責任鏈傳遞:請求沿著責任鏈依次傳遞,直到找到能夠處理請求的處理者為止;

  • 請求完成:當某個處理者成功處理請求後,責任鏈模式可以選擇終止傳遞或繼續傳遞給下一個處理者。

五、例項分析

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 結構體可以理解為一箇中間物件,其實現了 ExecuteSetNext 方法,避免了在 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")
}

六、結論

  • 責任鏈模式透過將請求的傳送者和接收者解耦,提供了一種靈活且可擴充套件的處理方式;它能夠動態地組織和分配責任,使得系統更易於維護和擴充套件;

  • 責任鏈模式的核心思想是「鏈式處理」,在處理複雜業務流程時特別有用,它不僅提高了系統的靈活性和可擴充套件性,同時也使得程式碼更加清晰和易於理解;

  • 在實際應用中,責任鏈模式常用於處理請求的優先順序排序、許可權校驗、日誌記錄等場景,為複雜系統的請求處理提供了一種有效的解決方案;

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.