淺談責任鏈(Chain of Responsibility)和裝飾器(Decorator)模式的差別
裝飾器模式和責任鍊模式,都是很常見用來把程式碼邏輯切開,讓不同元件能更明確用code表達其負責的邏輯的方法。
以下先簡單介紹一下這兩種模式
責任鏈模式(Chain of Responsibility)
接收輸入物件並沿鏈傳遞請求,直到其中一個handler處理它為止。 從重構的角度來看,當發現code的邏輯可以被切成不同的角色負責的邏輯時,可以重構成責任鍊的形式,分成不同的角色Class,讓各自的邏輯交由不同角色Class來處理。
範例
要實作一個HandlerRequest,它可以處理Auth, Log, Data三種類型的Request 使用的時候要像下面例子這樣使用。
func main() {
req1 := &Request{Type: "Auth", Payload: "User credentials"}
req2 := &Request{Type: "Log", Payload: "User activity"}
req3 := &Request{Type: "Data", Payload: "User data"}
fmt.Println(HandleRequest(req1))
fmt.Println(HandleRequest(req2))
fmt.Println(HandleRequest(req3))
}
未使用責任鍊模式的HandlerRequest
func HandleRequest(request *Request) string {
if request.Type == "Auth" {
return fmt.Sprintf("Handling authentication: %s", request.Payload)
} else if request.Type == "Log" {
return fmt.Sprintf("Logging data: %s", request.Payload)
} else if request.Type == "Data" {
return fmt.Sprintf("Processing data: %s", request.Payload)
} else {
return fmt.Sprintf("Unknown request type: %s", request.Type)
}
}
使用責任鏈模式的HandlerRequest
要先定義interface & Base struct
// Handler 定義處理器接口
type Handler interface {
Handle(request *Request) string
}
// BaseHandler 實作責任鍊的pass邏輯
type BaseHandler struct {
next Handler
}
//只在其他Handler的Constructor中使用
func (h *BaseHandler) setNext(next Handler) {
h.next = next
}
func (h *BaseHandler) handleNext(request *Request) string{
if h.next != nil {
return h.next.Handle(request)
}
return ""
}
接著實作AuthHandler, LogHandler, DataHandler
AuthHandler:
// AuthHandler 處理認證請求,並且有自己的參數
type AuthHandler struct {
BaseHandler
authKey string
}
func NewAuthHandler(authKey string, next Handler) *AuthHandler {
handler := &AuthHandler{
authKey: authKey,
}
handler.setNext(next) // 設置下一個處理器
return handler
}
func (h *AuthHandler) Handle(request *Request) string {
if request.Type == "Auth" {
return fmt.Sprintf("Handling authentication with key: %s, Payload: %s", h.authKey, request.Payload)
} else {
return h.handleNext(request)
}
}
LogHandler:
// LogHandler 處理日誌請求,並且有自己的參數
type LogHandler struct {
BaseHandler
logLevel string
}
func NewLogHandler(logLevel string, next Handler) *LogHandler {
handler := &LogHandler{
logLevel: logLevel,
}
handler.setNext(next) // 設置下一個處理器
return handler
}
func (h *LogHandler) Handle(request *Request)string {
if request.Type == "Log" {
fmt.Sprintf("Logging at level: %s, Payload: %s", h.logLevel, request.Payload)
} else {
return h.handleNext(request)
}
}
DataHandler:
// DataHandler 處理數據請求,並且有自己的參數
type DataHandler struct {
BaseHandler
dataSource string
}
func NewDataHandler(dataSource string, next Handler) *DataHandler {
handler := &DataHandler{
dataSource: dataSource,
}
handler.setNext(next) // 設置下一個處理器
return handler
}
func (h *DataHandler) Handle(request *Request) string {
if request.Type == "Data" {
return fmt.Sprintf("Processing data from source: %s, Payload:%s", h.dataSource, request.Payload)
} else {
return h.handleNext(request)
}
}
最後,在main()需要多加上把不同的handler串成一個鍊(這邊是使用constructor注入的方式設定)
func main() {
// 初始化處理器
authHandler := NewAuthHandler()
logHandler := NewLogHandler(authHandler)
dataHandler := NewDataHandler(logHandler)
// 創建請求
req1 := &Request{Type: "Auth", Payload: "User credentials"}
req2 := &Request{Type: "Log", Payload: "User activity"}
req3 := &Request{Type: "Data", Payload: "User data"}
// 處理請求
fmt.Println(authHandler.Handle(req1))
fmt.Println(authHandler.Handle(req2))
fmt.Println(authHandler.Handle(req3))
}
這樣做的優勢是,如果要修改Auth相關的邏輯,只要到AuthHandler去改即可。
如果沒有使用責任鏈,當HandlerRequest內部使用的handler數量變得很大,並且決定要不要使用某個handler的邏輯很複雜時,邏輯甚至會互相糾結,讓code就會變得非常的難修改和維護。
裝飾器模式(Decorator)
在現已有的邏輯上,外掛上額外的邏輯。
範例
一樣是上面的requestHandler,但現在要加上一個額外的功能:不論處理成功或失敗,都要把結果記錄寫成log。
這個外掛的功能就可以用decorator來實作,它並不會影響到核心功能,只是額外加其它功能上去。
// LogDecorator 處理數據請求,並且有自己的參數
type LogDecorator struct {
BaseHandler
}
func NewLogDecorator(next Handler) *DataHandler {
handler := &LogDecorator{}
handler.setNext(next) // 設置下一個處理器
return handler
}
func (h *LogDecorator) Handle(request *Request) {
if h.handlerNext(request) != "" {
log.Info("request success")
}else{
log.Info("request failed")
}
}
結論
從實作方面來看,責任鏈和裝飾器的code是大同小異,都是藉由實作相同的interface,把多個邏輯拆分到不同的物件上處理。最後把Object依照順序一層層的注入到其它Object上,形成一個執行的鏈。它們唯一的差別是兩者切分邏輯的方式。
責任鏈是鏈上的每個Object都能獨立完成一種使用情境的邏輯;而裝飾器則是為核心邏輯外掛其它額外的功能。
當有核心功能存在,需要外掛附加功能時,這個做法就可以稱為裝飾器模式;而當有不同的handler, 並且每個都可以獨立處理完某一種使用情境時,就稱其為責任鏈模式。一切都是看想要切分的邏輯,是切出核心邏輯和外掛邏輯(裝飾器), 還是切出不同的職責,每個都是一種情境的核心邏輯(責任鏈)。他們的形式可以長得一樣,差別只在於他們切分產生出來的handler能否獨立完成一種工作。