设计模式学习总结:责任链模式
本文为笔者在阅读一些书籍、博客、专栏等资料后所总结的个人对于责任链模式的笔记,由于笔者才疏学浅,若有不足之处,还望各位加以斧正,您的建议与鼓励都是笔者源源不断的前进动力。感谢!
文章大纲如下:
- 责任链模式的简单认识
- 责任链模式的简单应用
- 两种责任链的实现方法:基于数组、链表
- 责任链的一些简单应用
- 责任链模式在源码中的体现
- 责任链模式的优缺点
责任链模式简单认识
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. ---- 《设计模式》GoF
中文意思为:
通过给多个对象处理请求的机会,避免请求的发送方与其接收方耦合。将接收对象串起来,并沿着链传递请求,直到有一个对象处理它。
笔者的将其理解为:
现在有一条责任链(其中包含着各种接收对象,也可以理解为处理器,其具有处理请求的方法)。发送方对责任链发送请求,责任链中的对象A接收到请求后,若其能处理该请求,则处理,否则就向下一个对象传递请求,直到有一个对象能对其进行处理。整个过程形成一条链。
此外,责任链模式还有另一种实现方式,即
请求进入责任链,对象A接收到请求后,若需要处理,则处理,处理后传递给下一个对象;若不需要处理,则直接传递给下一个对象。也就是说,其最终都会将该对象传递给链上的下一个对象,以此类推,直到最后一个对象处理后,方结束。此变体中的请求会被责任链中的每个处理器(对象)处理。
责任链模式的简单应用
这两种责任链的实现方法
以下实现代码参考自王争先生在极客时间上的《设计模式之美》所展示的代码片段。我在其中注释部分加入了自己的理解、总结。
第一种责任链(即处理后就返回)
此责任链的实现方法有两种,分别是数组实现和链表实现。
-
数组实现
数组实现的大致模板:
/**
* 处理者接口,定义了处理者的行为
*/
interface IHandler {
boolean handle();
}
/**
* 处理者A
*/
class HandlerA implements IHandler {
@Override
public boolean handle() {
boolean status = true;
// 此处省略处理任务的方法……
// 如果任务被处理了,就令status为true;否则令其为false
System.out.println("[HandlerA]: status is " + status);
return status;
}
}
/**
* 处理者B
*/
class HandlerB implements IHandler {
@Override
public boolean handle() {
boolean status = true;
// 此处省略处理任务的方法……
// 如果任务被处理了,就令status为true;否则令其为false
System.out.println("[HandlerB]: status is " + status);
return status;
}
}
/**
* 责任链
*/
class HandlerChain {
// 责任链的数组存储对象
private List<IHandler> handlerList = new ArrayList<>();
// 向数组中添加handler的方法,添加成功返回true,反之为false
public boolean addHandler(IHandler handler) {
return this.handlerList.add(handler);
}
// 责任链调用处理者去处理请求
public void handle() {
// 遍历责任链上的处理者
for (IHandler handler : handlerList) {
boolean status = handler.handle();
// 如果处理者返回的是true,就说明已经处理请求了,可以退出遍历了
// 反之,继续遍历,直到被处理
if (status == true) {
break;
}
}
}
}
/**
* 基于数组实现的责任链(处理后就停止的责任链)
*/
public class Demo {
public static void main(String[] args) {
HandlerChain handlerChain = new HandlerChain();
handlerChain.addHandler(new HandlerA());
handlerChain.addHandler(new HandlerB());
handlerChain.handle();
}
}
输出结果为:
可见,HandleA处理了请求后,就不再往后传递请求了。
-
链表实现
链表实现的大致模板:
/**
* 处理者抽象类,定义了处理者的行为
*/
abstract class IHandler {
// 下一个处理者
protected IHandler nextHandler;
// 子类需要实现的处理方法
protected abstract boolean doHandle();
// 设置下一个处理者
public void setNextHandler(IHandler nextHandler) {
this.nextHandler = nextHandler;
}
// 获取下一个处理者
public IHandler getNextHandler() {
return this.nextHandler;
}
// 父类调用子类的处理方法判断是否向后传递请求
public final void handle() {
boolean status = doHandle();
if ((!status) && (this.nextHandler != null)) {
this.nextHandler.handle();
}
}
}
/**
* 处理者A
*/
class HandlerA extends IHandler {
@Override
public boolean doHandle() {
boolean status = false;
// 此处省略处理任务的方法……
// 如果任务被处理了,就令status为true;否则令其为false
System.out.println("[HandlerA]: status is " + status);
return status;
}
}
/**
* 处理者B
*/
class HandlerB extends IHandler {
@Override
public boolean doHandle() {
boolean status = true;
// 此处省略处理任务的方法……
// 如果任务被处理了,就令status为true;否则令其为false
System.out.println("[HandlerB]: status is " + status);
return status;
}
}
/**
* 责任链
*/
class HandlerChain {
// 责任链的头结点
private IHandler headHandler;
// 责任链的尾结点
private IHandler tailHandler;
// 向责任链中添加处理者
public void addHandler(IHandler handler) {
// 把处理者的下一个结点设为空
handler.setNextHandler(null);
// 如果头结点为空,说明责任链现在没有任何处理者,此时把头结点和尾结点都设置为函数传入的IHandler实例
if (this.headHandler == null) {
this.headHandler = handler;
this.tailHandler = handler;
return; // 设置好后就返回,因为后面要给head非空时的情况赋值
}
// headHandler非空时,把传进来的结点加入到链表尾部
this.tailHandler.setNextHandler(handler);
this.tailHandler = handler;
}
// 责任链调用处理者去处理请求
public void handle() {
if (this.headHandler != null) {
this.headHandler.handle();
}
}
}
/**
* 基于链表实现的责任链(处理后就停止的责任链)
*/
public class Demo {
public static void main(String[] args) {
HandlerChain handlerChain = new HandlerChain();
handlerChain.addHandler(new HandlerA());
handlerChain.addHandler(new HandlerB());
handlerChain.handle();
}
}
输出结果:
第二种责任链(一直往后传递)
此责任链的实现方法也是两种,即数组、链表实现。
-
数组实现
数组实现的大致模板:
/**
* 处理者接口,定义了处理者的行为
*/
interface IHandler {
// 实现类的处理方法
void handle();
}
/**
* 处理者A
*/
class HandlerA implements IHandler {
@Override
public void handle() {
// 此处省略处理任务的方法…
System.out.println("[HandlerA]: handle()");
}
}
/**
* 处理者B
*/
class HandlerB implements IHandler {
@Override
public void handle() {
// 此处省略处理任务的方法……
System.out.println("[HandlerB]: handle()");
}
}
/**
* 责任链
*/
class HandlerChain {
// 责任链的存储对象
List<IHandler> handlerList = new ArrayList<>();
// 向责任链中添加处理者
public HandlerChain addHandler(IHandler handler) {
handlerList.add(handler);
return this;
}
// 责任链调用处理者去处理请求
public void handle() {
for (IHandler handler : handlerList) {
handler.handle();
}
}
}
/**
* 基于数组实现的责任链(处理后就停止的责任链)
*/
public class Demo {
public static void main(String[] args) {
HandlerChain handlerChain = new HandlerChain();
IHandler handlerA = new HandlerA();
IHandler handlerB = new HandlerB();
handlerChain.addHandler(handlerA).addHandler(handlerB);
handlerChain.handle();
}
}
输出结果:
-
链表实现
链表实现的大致模板:
/**
* 处理者抽象类,定义了处理者的行为
*/
abstract class IHandler {
// 下一个处理者
protected IHandler nextHandler;
// 子类需要实现的处理方法
protected abstract void doHandle();
// 设置下一个处理者
public void setNextHandler(IHandler nextHandler) {
this.nextHandler = nextHandler;
}
// 获取下一个处理者
public IHandler getNextHandler() {
return this.nextHandler;
}
// 父类调用子类的处理方法并向后请求
public final void handle() {
doHandle();
// 与前一种责任链所不同的是,这里不需要通过前面是否处理判断是否传递,而是一直传递
if (this.nextHandler != null) {
System.out.println("[IHandler]-nextHandler: " + this.nextHandler.getClass());
this.nextHandler.handle();
}
}
}
/**
* 处理者A
*/
class HandlerA extends IHandler {
@Override
public void doHandle() {
// 此处省略处理任务的方法…
System.out.println("[HandlerA]: doHandler()");
}
}
/**
* 处理者B
*/
class HandlerB extends IHandler {
@Override
public void doHandle() {
// 此处省略处理任务的方法……
System.out.println("[HandlerB]: doHandler()");
}
}
/**
* 责任链
*/
class HandlerChain {
// 责任链的头结点
private IHandler headHandler;
// 责任链的尾结点
private IHandler tailHandler;
// 向责任链中添加处理者
public void addHandler(IHandler handler) {
// 把处理者的下一个结点设为空
handler.setNextHandler(null);
// 如果头结点为空,说明责任链现在没有任何处理者,此时把头结点和尾结点都设置为函数传入的IHandler实例
if (this.headHandler == null) {
this.headHandler = handler;
this.tailHandler = handler;
return; // 设置好后就返回,因为后面要给head非空时的情况赋值
}
// headHandler非空时,把传进来的结点加入到链表尾部
this.tailHandler.setNextHandler(handler);
this.tailHandler = handler;
}
// 责任链调用处理者去处理请求
public void handle() {
if (this.headHandler != null) {
this.headHandler.handle();
}
}
}
/**
* 基于链表实现的责任链(处理后就停止的责任链)
*/
public class Demo {
public static void main(String[] args) {
HandlerChain handlerChain = new HandlerChain();
handlerChain.addHandler(new HandlerA());
handlerChain.addHandler(new HandlerB());
handlerChain.handle();
}
}
输出结果:
可以看到,HandlerA在处理了请求后,又传递给了HandlerB,HandlerB也对请求进行了处理。
责任链的一些应用
一个小案例
假设有一场景如下:我们需要对用户的登录请求进行登陆前的数据校验、登录表单的数据校验、权限数据的校验。
在这样的场景中,我们通常会这样去实现(模拟实现,省略了部分操作、代码):
public class Demo {
public boolean login(User user) {
String userName = user.getUserName();
String password = user.getPassword();
// 非空校验(数据校验部分)
if (StringUtils.isEmpty(userName) && StringUtils.isEmpty(password)) {
System.out.println("数据校验不通过!");
return false;
}
System.out.println("数据校验通过!");
// 用户名、密码校验,模拟验证
if (userName != "xingzhi" && password != "987") {
System.out.println("用户名或密码错误!");
return false;
}
System.out.println("用户名、密码校验通过!");
user.setRoleName("visitor");
// 用户角色校验
if (user.getRoleName() != "admin") {
System.out.println("无权限登录!");
return false;
}
System.out.println("权限验证通过");
return true;
}
// 模拟登录接口调用
public static void main(String[] args) {
Demo demo = new Demo();
User user = new User().setUserName("xingzhi").setPassword("987");
if (demo.login(user)) {
System.out.println("登陆成功!");
} else {
System.out.println("登陆失败!");
}
}
}
// 模拟一个用户类
class User {
private String userName;
private String password;
private String roleName;
public String getUserName() {
return userName;
}
public User setUserName(String userName) {
this.userName = userName;
return this;
}
public String getPassword() {
return password;
}
public User setPassword(String password) {
this.password = password;
return this;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
输出结果如下:
这样确实把需求实现了,但是
根据这一场景,责任链模式是一个很好的选择。因为进行完一个校验后还需要后续的校验,所以我们选择使用第二种责任链的链表实现方式来完成需求(需要注意的是,当某一处理者不通过时,就不往下传递了,所以我在Ihandler接口类中新增了handlerStatus属性用来记录处理者的处理状态)。代码如下:(但我感觉我这个案例可能不太好.....)
/** 处理者父类 */
abstract class abstractHandler {
// 下一个处理者
protected abstractHandler nextHandler;
protected boolean handlerStatus;
// 子类需要实现的处理方法
protected abstract boolean doHandle(User user);
// 设置下一个处理者
public void setNextHandler(abstractHandler nextHandler) {
this.nextHandler = nextHandler;
}
// 获取下一个处理者
public abstractHandler getNextHandler() {
return this.nextHandler;
}
// 父类调用子类的处理方法并向后请求
public final boolean handle(User user) {
handlerStatus = doHandle(user);
if (!handlerStatus) return false;
// 与前一种责任链所不同的是,这里不需要通过前面是否处理判断是否传递,而是一直传递
if (this.nextHandler != null) {
System.out.println("[IHandler]-nextHandler: " + this.nextHandler.getClass());
this.nextHandler.handle(user);
}
return true;
}
}
/**
* 数据校验处理者
*/
class ValidateHandler extends abstractHandler {
@Override
public boolean doHandle(User user) {
System.out.println("[ValidateHandler]: doHandler()");
// 如果用户名和密码不为空,则进行处理
if (!StringUtils.isEmpty(user.getUserName()) && !StringUtils.isEmpty(user.getPassword())) {
System.out.println("数据校验成功!");
return true;
} else {
System.out.println("数据格式不通过!");
return false;
}
}
}
/**
* 身份校验处理者
*/
class AuthenticationHandler extends abstractHandler {
@Override
public boolean doHandle(User user) {
System.out.println("[AuthenticationHandler]: doHandler()");
if ("xingzhi".equals(user.getUserName()) && "987".equals(user.getPassword())) {
user.setRoleName("admin");
System.out.println("身份验证通过!");
return true;
} else {
System.out.println("身份验证不通过!");
return false;
}
}
}
/** 角色校验处理者 */
class RoleHandler extends abstractHandler {
@Override
public boolean doHandle(User user) {
System.out.println("[RoleHandler]: doHandler()");
if ("admin".equals(user.getRoleName())) {
System.out.println("橘色校验通过!");
return true;
} else {
System.out.println("角色校验不通过!");
return false;
}
}
}
/**
* 责任链
*/
class LoginHandlerChain {
// 责任链的头结点
private abstractHandler headHandler;
// 责任链的尾结点
private abstractHandler tailHandler;
// 向责任链中添加处理者
public LoginHandlerChain addHandler(abstractHandler handler) {
// 把处理者的下一个结点设为空
handler.setNextHandler(null);
// 如果头结点为空,说明责任链现在没有任何处理者,此时把头结点和尾结点都设置为函数传入的IHandler实例
if (this.headHandler == null) {
this.headHandler = handler;
this.tailHandler = handler;
return this; // 设置好后就返回,因为后面要给head非空时的情况赋值
}
// headHandler非空时,把传进来的结点加入到链表尾部
this.tailHandler.setNextHandler(handler);
this.tailHandler = handler;
return this;
}
// 责任链调用处理者去处理请求
public boolean handle(User user) {
if (this.headHandler != null) {
return this.headHandler.handle(user);
}
return false;
}
}
// 模拟一个用户类
class User {
private String userName;
private String password;
private String roleName;
private String Permission;
public String getUserName() {
return userName;
}
public User setUserName(String userName) {
this.userName = userName;
return this;
}
public String getPassword() {
return password;
}
public User setPassword(String password) {
this.password = password;
return this;
}
public String getRoleName() {
return roleName;
}
public User setRoleName(String roleName) {
this.roleName = roleName;
return this;
}
public String getPermission() {
return Permission;
}
public User setPermission(String permission) {
Permission = permission;
return this;
}
}
/**
* 基于链表实现的责任链(处理后就停止的责任链)
*/
public class Demo {
public static void main(String[] args) {
User user = new User()
.setUserName("xingzhi")
.setPassword("987");
LoginHandlerChain loginHandlerChain = new LoginHandlerChain()
.addHandler(new ValidateHandler())
.addHandler(new AuthenticationHandler())
.addHandler(new RoleHandler());
if (loginHandlerChain.handle(user)) {
System.out.println("登陆成功!欢迎您:" + user.getUserName());
} else {
System.out.println("登陆失败!");
};
}
}
运行结果如图:
在这个场景中使用责任链模式就可以满足开闭原则,提高代码的扩展性。
比如,现在需要在角色认证后进行权限认证,这时我们只需要增加一个权限验证类就行了,但是我这个User类还得改改....所以我这个案例举得不太好(有点又臭又长了)....仅供参考...哈哈...
/** 权限校验处理者 */
class PermissionHandler extends abstractHandler {
@Override
public boolean doHandle(User user) {
System.out.println("[PermissionHandler]: doHandler()");
if ("menu:add".equals(user.getPermission())) {
System.out.println("权限校验通过!");
return true;
} else {
System.out.println("权限校验不通过!");
return false;
}
}
}
责任链模式在源码中的体现
在看过了我的糟糕的案例后,我们来看看大师的作品,源码中的责任链模式:
/**
* 先留个坑...有时间了来填...
*/
责任链模式的优缺点
最后,总结下责任链模式的优缺点。
优点
- 将请求的发送者和接收者解耦。
- 简化对象,减少代码的复杂性。(处理者不需要知道责任链的内部构造以及如何处理的)
- 满足开闭原则,提高代码的扩展性。(动态地新增或删除责任)
缺点
- 不容易观察运行时的特征,出现问题不好排查。(如果某一节点出现问题,则容易造成系统崩溃)
- 并不一定保证请求一定会执行。(责任链过长时,请求没被处理或者处理时间过长,会影响整体性能)