行为型设计模式,像中间件、拦截器等框架组件都是应用的这种设计模式
场景:
挂号—>诊室看病—>收费处缴费—>药房拿药
实现责任链模式的对象最起码需要包含如下特性
- 成员属性
- nextHandler: 下一个等待被调用的对象实例
- 成员方法
- SetNext: 把下一个对象的实例绑定到当前对象的nextHandler属性上;
- Do: 当前对象业务逻辑入口,他是每个处理对象实现自己逻辑的地方;
- Execute: 负责职责链上请求的处理和传递;它会调用当前对象的Do,nextHandler不为空则调用nextHandler.Do;
接口和抽象类
由于 Go 并不支持继承,即使Next实现了Do方法,也不能达到在父类方法中调用子类方法的效果—即在我们的例子里面用Next 类型的Execute方法调用不到外部实现类型的Do方法。所以我们这里选择Next类型直接不实现Do方法,这也是在暗示这个类型是专门用作让实现类进行内嵌组合使用的。
type PatientHandler interface {
Execute(*patient) error
SetNext(PatientHandler) PatientHandler
Do(*patient) error
}
// 充当抽象类型,实现公共方法,抽象方法不实现留给实现类自己实现
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) {
// 调用不到外部类型的 Do 方法,所以 Next 不能实现 Do 方法
if n.nextHandler != nil {
if err = n.nextHandler.Do(patient); err != nil {
return
}
return n.nextHandler.Execute(patient)
}
return
}
职责链要处理的请求
//流程中的请求类--患者
type patient struct {
Name string
RegistrationDone bool
DoctorCheckUpDone bool
MedicineDone bool
PaymentDone bool
}
// 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 诊室处理器--用于医生给病人看病
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
}
// StartHandler 不做操作,作为第一个Handler向下转发请求
type StartHandler struct {
Next
}
// Do 空Handler的Do
func (h *StartHandler) Do(c *patient) (err error) {
// 空Handler 这里什么也不做 只是载体 do nothing...
return
}
使用
func main() {
patientHealthHandler := StartHandler{}
//
patient := &patient{Name: "abc"}
// 设置病人看病的链路
patientHealthHandler.SetNext(&Reception{}).// 挂号
SetNext(&Clinic{}). // 诊室看病
SetNext(&Cashier{}). // 收费处交钱
SetNext(&Pharmacy{}) // 药房拿药
//还可以扩展,比如中间加入化验科化验,图像科拍片等等
// 执行上面设置好的业务流程
if err := patientHealthHandler.Execute(patient); err != nil {
// 异常
fmt.Println("Fail | Error:" + err.Error())
return
}
// 成功
fmt.Println("Success")
}
另外职责链也可以设置中止条件,针对我们文中的例子就是在Execute方法里加判断,一旦满足中止后就不再继续往链路的下级节点传递请求。Gin 的中间件的abort方法就是按照这个原理实现的,同时这也是职责链跟装饰器模式的一个区别,装饰器模式无法在增强实体的过程中停止,只能执行完整个装饰链路。