责任链模式
责任链模式是一种行为设计模式,允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。
模型说明
- Handler:声明了所有具体处理者的通用接口。该接口通常仅包含单个方法用于请求处理,但有时其还包含一个设置链上下个处理者的方法。
- BaseHandler:是一个可选的类,你可以将所有处理者共用的样本代码放置在其中。
- 通常情况下,该类中定义了一个保存对于下个处理者引用的成员变量。客户端可通过将处理者传递给上个处理者的构造函数或设定方法来创建链。该类还可以实现默认的处理行为:确定下个处理者存在后再将请求传递给它。
- ConcreteHandlers:包含处理请求的实际代码。每个处理者接收到请求后,都必须决定是否进行处理,以及是否沿着链传递请求。
- 处理者通常是独立且不可变的,需要通过构造函数一次性地获得所有必要的数据。
- Client:可根据程序逻辑一次性或者动态地生成链。值得注意的是,请求可发送给脸上的任意一个处理者,而非必须是第一个处理者。
优缺点
1.优点
- 你可以控制请求处理的顺序。
- 单一职责原则:你可对发起操作和执行操作的类进行解耦。
- 开闭原则:你可以在不更改现有代码的情况下在程序中新增处理者。
2.缺点
- 部分请求可能未被处理。
使用场景
- 当程序需要使用不同方式处理不同种类请求, 而且请求类型和顺序预先未知时, 可以使用责任链模式。
- 当必须按顺序执行多个处理者时, 可以使用该模式。
- 如果所需处理者及其顺序必须在运行时进行改变, 可以使用责任链模式。
参考代码
看一个医院应用的责任链模式例子:病人来访时, 他们首先都会去前台, 然后是看医生、 取药, 最后结账。
// patient.go 病人 package main type Patient struct { name string registrationDone bool doctorCheckUpDone bool medicineDone bool paymentDone bool }
// department.go 处理者接口 package main type Department interface { execute(*Patient) setNext(Department) }
// reception.go 具体处理者 package main import "fmt" type Reception struct { next Department } func (r *Reception) execute(p *Patient) { if p.registrationDone { fmt.Println("Patient registration already done") r.next.execute(p) return } fmt.Println("Reception registering patient") p.registrationDone = true r.next.execute(p) } func (r *Reception) setNext(next Department) { r.next = next }
// doctor.go 具体处理者 package main import "fmt" type Doctor struct { next Department } func (d *Doctor) execute(p *Patient) { if p.doctorCheckUpDone { fmt.Println("Doctor checkup already done") d.next.execute(p) return } fmt.Println("Doctor checking patient") p.doctorCheckUpDone = true d.next.execute(p) } func (d *Doctor) setNext(next Department) { d.next = next }
// medical.go 具体处理者 package main import "fmt" type Medical struct { next Department } func (m *Medical) execute(p *Patient) { if p.medicineDone { fmt.Println("Medicine already given to patient") m.next.execute(p) return } fmt.Println("Medical giving medicine to patient") p.medicineDone = true m.next.execute(p) } func (m *Medical) setNext(next Department) { m.next = next }
// cashier.go 具体处理者 package main import "fmt" type Cashier struct { next Department } func (c *Cashier) execute(p *Patient) { if p.paymentDone { fmt.Println("Payment Done") } fmt.Println("Cashier getting money from patient patient") } func (c *Cashier) setNext(next Department) { c.next = next }
// main.go 客户端代码 package main func main() { cashier := &Cashier{} //Set next for medical department medical := &Medical{} medical.setNext(cashier) //Set next for doctor department doctor := &Doctor{} doctor.setNext(medical) //Set next for reception department reception := &Reception{} reception.setNext(doctor) patient := &Patient{name: "abc"} //Patient visiting reception.execute(patient) }
output:
Reception registering patient Doctor checking patient Medical giving medicine to patient Cashier getting money from patient patient