责任链模式(行为型模式)
模式概述
责任链模式是一种行为设计模式,它可以将请求沿着处理链条发送。
收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下一个处理者。
问题
某公司正在开发一款在线订购系统,希望对系统访问进行限制,只允许认证用户创建订单,而有管理权限的用户则拥有订单的完全访问权限。
这些检查必须依次进行,只要有用户凭据输入,系统就会尝试对用户进行认证。
但系统需求总是在不断变化,在此期间增加了好几个后续步骤:如验证、过滤、缓存等。
这让代码混乱不堪。
方案
责任链模式建议将这些处理者连成一串链条,链条上的每个处理者都保存有下一处理者的引用。
除了处理请求外,处理者还负责沿着链传递请求。请求会在链上移动,直至所有处理者都有机会对其进行处理。
在上面的订购系统中,处理者会在处理完毕后决定是否继续沿着链传递请求。所有处理者都将执行自己的主要行为,无论该行为是身份验证还是数据缓存。

结构

实现
Java
/**
* 处理抽象类
*
*/
public abstract class Handler {
private Handler next;
public static Handler link(Handler first, Handler... chain) {
Handler handler = first;
for (Handler nextInChain: chain) {
head.next = nextInChain;
head = nextInChain;
}
return first;
}
public abstract boolean check(String email, String password);
protected boolean checkNext(String email, String password) {
if (next == null) {
return true;
}
return next.check(email, password);
}
}
/**
* 处理数量限制
*
*/
public class ThrottlingHandler extends Handler {
private int requestPerMinute;
private int request;
private long currentTime;
public ThrottlingHandler(int requestPerMinute) {
this.requestPerMinute = requestPerMinute;
this.currentTime = System.currentTimeMillis();
}
public boolean check(String email, String password) {
if (System.currentTimeMillis() > currentTime + 60_000) {
request = 0;
currentTime = System.currentTimeMillis();
}
request++;
if (request > requestPerMinute) {
System.out.println("请求被限制");
Thread.currentThread().stop();
}
return checkNext(email, password);
}
}
/**
* 处理登录信息
*
*/
public class UserLoginHandler extends Handler {
private Server server;
public UserLoginHandler(Server server) {
this.server = server;
}
public boolean check(String email, String password) {
if (!server.hasEmail(email)) {
System.out.println("邮件未被注册");
return false;
}
if (!server.isValidPassword(email, password)) {
System.out.println("密码错误");
return false;
}
return checkNext(email, password);
}
}
/**
* 授权目标
*
*/
public class Server {
private Map<String, String> users = new HashMap<>();
private Handler handler;
public void setHandler(Handler handler) {
this.handler = handler;
}
public boolean login(String email, String password) {
if (handler.check(email, password)) {
System.out.println("授权成功");
// ......
return true;
}
return false;
}
public void register(String email, String password) {
users.put(email, password);
}
public boolean hasEmail(String email) {
return users.containsKey(email);
}
public boolean validPassword(String email, String password) {
return users.get(email).equals(password);
}
}
/**
* 客户端
*
*/
public class Client {
private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
private static Server server;
private static void init() {
server = new Server();
server.register("admin@example.com", "123456");
server.register("user@example.com", "654321");
Handler handler = Handler.link(
new ThrottlingHandler(2),
new UserLoginHandler(server)
);
server.setHandler(handler);
}
public static void main(String[] args) throws IOException {
init();
boolean success;
do {
System.out.print("输入邮件:");
String email = reader.readLine();
System.out.print("输入密码:");
String password = reader.readLine();
success = server.logIn(email, password);
} while (!success);
}
}
Go
package main
import "fmt"
/**
* 部门处理者接口
*
*/
type Department interface {
execute(*Patient)
setNext(Department)
}
/**
* 具体处理者:前台
*
*/
type Reception struct {
next Department
}
func (r *Reception) execute(p *Patient) {
if p.registrationDone {
fmt.Println("患者登记已完成")
r.next.execute(p)
return
}
fmt.Println("接收患者登记")
p.registrationDone = true
r.next.execute(p)
}
func (r *Reception) setNext(next Department) {
r.next = next
}
/**
* 具体处理者:医生
*
*/
type Doctor struct {
next Department
}
func (d *Doctor) execute(p *Patient) {
if p.doctorCheckUpDone {
fmt.Println("医生检查已完成")
d.next.execute(p)
return
}
fmt.Println("医生检查患者")
p.doctorCheckUpDone = true
d.next.execute(p)
}
func (d *Doctor) setNext(next Department) {
d.next = next
}
/**
* 具体处理者:药房
*
*/
type Medical struct {
next Department
}
func (m *Medical) execute(p *Patient) {
if p.medicineDone {
fmt.Println("已给患者开药")
m.next.execute(p)
return
}
fmt.Println("开药给患者")
p.medicineDone = true
m.next.execute(p)
}
func (m *Medical) setNext(next Department) {
m.next = next
}
/**
* 具体处理者:收银
*
*/
type Cashier struct {
next Department
}
func (c *Cashier) execute(p *Patient) {
if p.paymentDone {
fmt.Println("已付款")
}
fmt.Println("患者支付现金")
}
func (c *Cashier) setNext(next Department) {
c.next = next
}
/**
* 患者
*
*/
type Patient struct {
name string
registrationDone bool
doctorCheckUpDone bool
medicineDone bool
paymentDone bool
}
/**
* 客户端
*
*/
func main() {
cashier := &Cashier{}
// 药房的下一个是收银
medical := &Medical{}
medical.setNext(cashier)
// 医生的下一个是药房
doctor := &Doctor{}
doctor.setNext(medical)
// 前台的下一个是医生
reception := &Reception{}
reception.setNext(doctor)
patient := &Patient{name: "abc"}
// 患者就诊
reception.execute(patient)
}
适用场景
当需要使用不同方式处理不同种类请求,而且请求具有一定顺序且类型未知时,可以使用责任链模式。
如果所需处理者及其顺序需要在运行时进行改变,可以使用责任链模式。
优缺点
责任链模式的优点。
可对发起操作和执行操作的类进行解耦,符合
单一职责原则
。可以在不更改程序的情况下新增处理者,符合
开闭原则
。可以控制请求处理的顺序。
责任链模式的缺点。
- 部分请求可能永远不会被处理。
相关性
责任链模式
、命令模式
、中介者模式
和观察者模式
都用于处理请求发送者和接收者之间的不同连接方式。责任链模式
的管理者可使用命令模式
实现,此时可以对请求代表的同一个上下文对象执行许多不同的操作。责任链模式
通常和组合模式
结合使用:当叶子组件接收到请求后,可将请求沿包含父组件的链条一直传递至树根。责任链模式
和修饰器模式
的类结构比较相似。两者都将需要执行的操作传递给一系列对象。但区别在于责任链模式
可以相互独立地执行所有操作,还可以随时停止传递请求。而修饰器模式
则无法中断请求的传递。
感谢支持
更多内容,请移步《超级个体》。