一、问题
业务扩张与缩减过程中,对应地就会有功能的增减,如果每次功能的增减都需要改动非常多的代码,就会导致代码逻辑不够清晰易懂,业务逻辑混乱的问题;
在实际开发中,将整个业务流程拆分不同的子流程,对应地每个代码块节点处理一个子流程,最后将这些代码块串起来执行,即可对应整个业务流程;
当该条业务有子流程增减时,只需要增减对应的代码块节点即可,这就是应用了责任链模式;
二、基本介绍
责任链模式是一种行为设计模式,用于将请求的发送者和接收者解耦,通过多个处理节点对请求的处理,将请求的传递与处理链条化;
在实际应用中,责任链模式常用于处理复杂的请求处理流程,使得请求能够沿着处理链依次传递,每个处理节点负责判断能否处理请求并进行处理或转发;
每个处理节点对应一个业务子流程,当业务子流程有增减时只需要对应的增减处理节点;
三、模式结构
责任链模式主要包含接口、子节点实现、数据类型、客户端四部分:
接口:定义处理请求数据的方法,通常包括具体的数据处理方法、责任链转移方法和子节点链接方法;
子节点实现:实现接口定义的方法,负责处理请求数据和节点转移;
数据类型:定义请求与返回的数据结构,各个节点从该结构中读取要处理的数据,以及写回处理后的数据;
客户端:创建并组装责任链,向责任链的起始点提交请求数据;
四、工作原理
请求提交:客户端创建请求数据并组装责任链,将请求数据提交给责任链的第一个处理者(即起始点);
处理判断:每个子节点处理者根据自身的业务逻辑判断是否能够处理该请求;如果可以处理,则处理请求并写回结果,遇到错误时可以提前返回;如果不能处理,则将请求传递给责任链中的下一个处理者;
责任链传递:请求沿着责任链依次传递,直到找到能够处理请求的处理者为止;
请求完成:当某个处理者成功处理请求后,责任链模式可以选择终止传递或继续传递给下一个处理者。
五、实例分析
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") }
六、结论
责任链模式通过将请求的发送者和接收者解耦,提供了一种灵活且可扩展的处理方式;它能够动态地组织和分配责任,使得系统更易于维护和扩展;
责任链模式的核心思想是「链式处理」,在处理复杂业务流程时特别有用,它不仅提高了系统的灵活性和可扩展性,同时也使得代码更加清晰和易于理解;
在实际应用中,责任链模式常用于处理请求的优先级排序、权限校验、日志记录等场景,为复杂系统的请求处理提供了一种有效的解决方案;