责任链模式
简介
责任链模式(Chain of Responsibility Pattern)允许开发者将请求沿着链进行发送,直到其中一个处理者对象对其进行处理。
责任链模式的角色说明:
- 处理者(Handler):声明所有具体处理类的通用接口。该接口通常仅包含一个方法,用于处理请求,但有时还会包含一个用于设置下一个具体处理者对象的方法。
- 基础处理者(Base Handler):可选类,可将所有处理者对象共用的样本代码放置在其中
- 具体处理者(ConcreteHandler)
- 客户端(Client)
使用场景
- 责任链模式适用于有多个候选选项处理相同请求的情形,适用于不希望客户端选择接受者(因为多个对象都可以处理请求)的情形,还适用于需要将客户端与接受者解耦的情形,客户端只需链中的 首个元素。
- 核心使用场景是当需要多个对象按顺序处理请求,且处理流程可能动态变化时。
- 多级审批/处理流程,例如医院就诊流程,挂号->诊室->药房->收费
- 需要运行时动态调整处理顺序或增减处理器。通过指定下一节点的方法来灵活调整责任链。
- 需要多个过滤器按顺序处理数据流
优点
- 请求处理顺序可控。
- 符合单一职责原则。可以对发起操作和执行操作的类进行解耦。
- 符合开闭原则。开发者可以在不更改现有代码的情况下在程序中添加处理者对象。
- 提高对象分配职责的灵活性。通过更改链中的成员或更改顺序,允许动态添加或删除处理者对象。增加请求处理新类非常方便。
- 责任链模式可以简化对象,对象不需要知道链结构。
缺点
- 部分请求可能未被处理。如果前一个处理者没能正常处理,后一个处理者就有可能无法接收到请求。
- 调试复杂度高,异常发生时可能需要逐级回溯链路。
- 有循环引用风险。
- 有性能损耗,可能每个处理者对象都要经手判断请求。
示例代码
Go
package chainofresponsibility
import "fmt"
// 基础处理者类 Patient 病人类
type Patient struct {
Name string
RegistrationDone bool
ClinicCheckUpDone bool
DrugstoreDone bool
PaymentDone bool
}
// 处理者接口 department 科室接口
type department interface {
Operate(*Patient)
SetNext(department)
}
// 具体处理者, 前台类
type Reception struct {
next department
}
func (r *Reception) Operate(p *Patient) {
if p.RegistrationDone {
fmt.Println("Patient already registered")
r.next.Operate(p)
return
}
fmt.Println("Reception registering patient")
p.RegistrationDone = true
r.next.Operate(p)
}
func (r *Reception) SetNext(next department) {
r.next = next
}
// 具体处理者, 诊室类
type Clinic struct {
next department
}
func (c *Clinic) Operate(p *Patient) {
if p.ClinicCheckUpDone {
fmt.Println("Patient already checked up")
c.next.Operate(p)
return
}
fmt.Println("Clinic checking up patient")
p.ClinicCheckUpDone = true
c.next.Operate(p)
}
func (c *Clinic) SetNext(next department) {
c.next = next
}
// 具体处理者, 药房类
type Drugstore struct {
next department
}
func (d *Drugstore) Operate(p *Patient) {
if p.DrugstoreDone {
fmt.Println("Patient already drugstored")
d.next.Operate(p)
return
}
fmt.Println("Drugstore giving medicine to patient")
p.DrugstoreDone = true
d.next.Operate(p)
}
func (d *Drugstore) SetNext(next department) {
d.next = next
}
// 具体处理者, 收银台类
type Cashier struct {
next department
}
func (c *Cashier) Operate(p *Patient) {
if p.PaymentDone {
fmt.Println("Patient already paid")
return
}
fmt.Println("Cashier taking money from patient")
p.PaymentDone = true
}
func (c *Cashier) SetNext(next department) {
c.next = next
}
测试代码
package chainofresponsibility
import (
"testing"
)
func TestChainOfResponsibility(t *testing.T) {
cashier := &Cashier{}
drugstore := &Drugstore{}
clinic := &Clinic{}
reception := &Reception{}
reception.SetNext(clinic)
clinic.SetNext(drugstore)
drugstore.SetNext(cashier)
reception.Operate(&Patient{Name: "Alan"})
}
Python
from abc import ABCMeta, abstractmethod
class Patient:
"""基础处理类"""
def __init__(
self,
name: str,
is_registration: bool = False,
is_clinic_check: bool = False,
is_drugstore: bool = False,
is_payment: bool = False,
):
self.name = name
self.is_registration = is_registration
self.is_clinic_check = is_clinic_check
self.is_drugstore = is_drugstore
self.is_payment = is_payment
class Department(metaclass=ABCMeta):
"""抽象基类, 科室接口"""
def __init__(self):
self.next: Department = None
@abstractmethod
def operate(self, patient: Patient):
pass
def set_next(self, depr):
if not issubclass(type(depr), Department):
raise Exception("Invalid Department")
self.next = depr
class Registration(Department):
"""具体处理类, 科室"""
def __init__(self):
super().__init__()
def operate(self, p: Patient):
if p.is_registration:
print(f"{p.name} is already registered")
self.next.operate(p)
return
print(f"{p.name} is registering")
p.is_registration = True
self.next.operate(p)
class Clinic(Department):
"""具体处理类, 诊室"""
def __init__(self):
super().__init__()
def operate(self, p: Patient):
if p.is_clinic_check:
print(f"{p.name} is already checked at the clinic")
self.next.operate(p)
return
print(f"{p.name} is checking clinic")
p.is_clinic_check = True
self.next.operate(p)
class DrugStore(Department):
"""具体处理类, 药房"""
def __init__(self):
super().__init__()
def operate(self, p: Patient):
if p.is_drugstore:
print(f"{p.name} is already given drugs")
self.next.operate(p)
return
print(f"{p.name} is checking drugstore")
p.is_drugstore = True
self.next.operate(p)
class Cashier(Department):
"""具体处理类, 收费处"""
def __init__(self):
super().__init__()
def operate(self, p: Patient):
if p.is_payment:
print(f"{p.name} is already paid")
return
print(f"{p.name} is paying")
p.is_payment = True
print(f"{p.name} is being released")
if __name__ == "__main__":
registration = Registration()
clinic = Clinic()
drugstore = DrugStore()
cashier = Cashier()
registration.set_next(clinic)
clinic.set_next(drugstore)
drugstore.set_next(cashier)
patient = Patient("John")
registration.operate(patient)
参考
- 廖显东《Go语言设计模式》