设计模式 | 飞机票 |
---|---|
三大工厂模式 | 登机入口 |
策略模式 | 登机入口 |
委派模式 | 登机入口 |
模板方法模式 | 登机入口 |
观察者模式 | 登机入口 |
单例模式 | 登机入口 |
原型模式 | 登机入口 |
代理模式 | 登机入口 |
装饰者模式 | 登机入口 |
适配器模式 | 登机入口 |
建造者模式 | 登机入口 |
责任链模式 | 登机入口 |
享元模式 | 登机入口 |
组合模式 | 登机入口 |
门面模式 | 登机入口 |
桥接模式 | 登机入口 |
中介者模式 | 登机入口 |
迭代器模式 | 登机入口 |
状态模式 | 登机入口 |
解释器模式 | 登机入口 |
备忘录模式 | 登机入口 |
命令模式 | 登机入口 |
访问者模式 | 登机入口 |
软件设计7大原则和设计模式总结 | 登机入口 |
上一篇,我们介绍了建造者模式,以及建造者模式在源码中的运用,今天我们会先介绍一下责任链模式,然后会再通过一个示例来将责任链模式和建造者模式结合起来应用。
责任链模式(Chain of Responsibility Pattern)是指将链中的每一个节点看作是一个对象,每个节点处理的请求均不同,且每个节点内部自动维护了一个下一个节点对象。当一个请求在链路的头部发出时,会沿着链的路径依次传递给每一个节点对象,直到有对象处理这个请求为止。
责任链模式属于行为型模式。
Talk is cheap,Show me the code。我们就以一个登录校验账号密码,角色,权限等信息的功能为例,直接来看一下责任链模式是怎么写的。
首先我们创建一个登录用户信息类:
package com.zwx.design.pattern.chainOfResponsibility;
public class LoginUser {
private String loginName;
private String password;
private String roleName;
private String permission;
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
}
创建一个Handler抽象类,这个类维护了链路中的下一个对象,并且将真正处理逻辑的方法doHandler只进行了抽象定义,具体留给实现类去实现:
package com.zwx.design.pattern.chainOfResponsibility;
public abstract class MyHandler {
protected MyHandler next;
public void next(MyHandler handler){
this.next = handler;
}
public abstract void doHandler(LoginUser loginUser);
}
接下来就是创建具体的实现类来实现MyHandler类。链中的每个节点只处理一件事,所以这个示例中我们可以拆分分三个节点,一个校验账号密码,一个校验角色,一个校验角色。
创建一个节点用来校验账号密码:
package com.zwx.design.pattern.chainOfResponsibility;
import org.apache.commons.lang3.StringUtils;
public class VerifyAccountHandler extends MyHandler {
@Override
public void doHandler(LoginUser loginUser) {
if (StringUtils.isBlank(loginUser.getLoginName())){
System.out.println("用户名不能为空");
return;
}
if (StringUtils.isBlank(loginUser.getPassword())){
System.out.println("密码不能为空");
return;
}
if (!loginUser.getPassword().equals("123456")){
System.out.println("密码不正确");
return;
}
System.out.println("账号密码校验通过");
next.doHandler(loginUser);
}
}
注意最后一句话,next.doHandler(loginUser)是用来调用链路中下一个节点的处理方法
新增一个校验角色Handler
package com.zwx.design.pattern.chainOfResponsibility;
public class VerifyRoleHanlder extends MyHandler {
@Override
public void doHandler(LoginUser loginUser) {
if(!"admin".equals(loginUser.getRoleName())){
System.out.println("角色信息有误");
return;
}
System.out.println("角色信息校验通过");
next.doHandler(loginUser);
}
}
同样的,这里也需要调用下一个节点的处理方法
新增一个校验权限的Handler:
package com.zwx.design.pattern.chainOfResponsibility;
public class VerifyPermissionHanlder extends MyHandler {
@Override
public void doHandler(LoginUser loginUser) {
if (!"admin".equals(loginUser.getPermission())){
System.out.println("暂无权限");
return;
}
System.out.println("权限校验通过,登录成功");
}
}
因为permission已经是最后一个节点,所以这里不需要再继续制定下一个节点了,内部也没有再维护下一个节点对象了。
现在让我们来看一下应该如何调用上面的示例:
package com.zwx.design.pattern.chainOfResponsibility;
public class TestChain {
public static void main(String[] args) {
MyHandler accountHandler = new VerifyAccountHandler();
MyHandler roleHanlder = new VerifyRoleHanlder();
MyHandler permissionHanlder = new VerifyPermissionHanlder();
accountHandler.next(roleHanlder);
roleHanlder.next(permissionHanlder);
LoginUser loginUser = new LoginUser();
loginUser.setLoginName("孤狼1号");
loginUser.setPassword("123");
loginUser.setRoleName("admin");
loginUser.setPermission("admin");
accountHandler.doHandler(loginUser);//从起点开始调用
}
}
输出结果:
密码不正确
如果将密码修改为正确密码123456,则输出如下结果:
账号密码校验通过
角色信息校验通过
权限校验通过,登录成功
我们先来看下传统的这种登录逻辑的写法:
package com.zwx.design.pattern.chainOfResponsibility;
import org.apache.commons.lang3.StringUtils;
public class LoginService {
public void login(LoginUser loginUser){
//1.校验账号密码
if (StringUtils.isBlank(loginUser.getLoginName())){
System.out.println("用户名不能为空");
return;
}
if (StringUtils.isBlank(loginUser.getPassword())){
System.out.println("密码不能为空");
return;
}
if (!loginUser.getPassword().equals("123456")){
System.out.println("密码不正确");
return;
}
//2.角色
if(!"admin".equals(loginUser.getRoleName())){
System.out.println("角色信息有误");
return;
}
//3.校验权限
if (!"admin".equals(loginUser.getPermission())){
System.out.println("暂无权限");
return;
}
System.out.println("校验通过,登录成功");
}
}
看起来写法上似乎比通过责任链模式写法简单明了,但是一堆业务代码全部堆在一起,而且我们示例中的逻辑校验比较简单,如果逻辑变得很复杂,那么将各种逻辑校验做一个解耦拆分对后期维护是非常有利的。
上面的示例写法中,最后在调用过程中有点不是很优雅,由此我们联想到了建造者模式的链式写法,接下来让我们结合建造者模式来对其进行改写。
改写时我们只需要对顶层抽象类进行改写:
package com.zwx.design.pattern.chainOfResponsibility.build;
import com.zwx.design.pattern.chainOfResponsibility.LoginUser;
public abstract class BuildHandler<T> {
protected BuildHandler next;
public void next(BuildHandler handler){
this.next = handler;
}
public abstract void doHandler(LoginUser loginUser);
public static class Builder<T>{
private BuildHandler<T> head;
private BuildHandler<T> tail;
public Builder<T> addHanlder(BuildHandler handler){
if (null == head){
//head==null表示第一次添加到队列
head = this.tail = handler;
return this;
}
this.tail.next(handler);//原tail节点指向新添加进来的节点
this.tail = handler;//新添加进来的节点设置为tail节点
return this;
}
public BuildHandler<T> build(){
return this.head;
}
}
}
这个类中,我们通过一个静态内部类Builder来讲链构造成一个队列。
其他三个类只需要实现改造之后的BuilderHanlder类,其他不需要修改,那么我们来看看现在的测试类又应该如何调用:
package com.zwx.design.pattern.chainOfResponsibility.build;
import com.zwx.design.pattern.chainOfResponsibility.*;
public class TestBuildChain {
public static void main(String[] args) {
LoginUser loginUser = new LoginUser();
loginUser.setLoginName("孤狼1号");
loginUser.setPassword("123456");
loginUser.setRoleName("admin");
loginUser.setPermission("admin");
BuildHandler.Builder builder = new BuildHandler.Builder();
builder.addHanlder(new VerifyAccountHandler())
.addHanlder(new VerifyRoleHanlder())
.addHanlder(new VerifyPermissionHanlder());
builder.build().doHandler(loginUser);
}
}
输出结果:
账号密码校验通过
角色信息校验通过
权限校验通过,登录成功
可以看到,改写之后在调用时会优雅很多。
从上面的示例中,可以明确,责任链模式只有两个角色:
责任链模式主要是解耦了请求与处理,用户只需要将请求发送到链上即可,无需关心请求的具体内容和处理细节,请求会自动进行传递直至有节点进行处理。可以适用于如下场景:
责任链模式应用比较广泛的就是拦截器。
我们先一下Servlet中的J2EE规范定义的一个拦截器接口:
我们发现这脸只有一个doFilter方法,并没有维护一个链里面的下一个对象。那么这个是怎么实现链路传递的呢?
我们看一下Spring的实现MockFilterChain:
从上面两段代码可以发现,子类通过一个List来构建“链路”,最终调用的时候就是通过遍历List来实现“链路”传递。
任何一个设计模式都有优点和缺点,那么责任链模式有何优缺点呢?
本文介绍了责任链模式的基本用法,并通过一个示例将责任链模式和建造者模式结合起来使用,使得代码更加优雅,同时也介绍了Spring中对责任链模式的应用,希望通过本文的学习,大家可以更好地理解责任链模式的原理,可以在合适的场景中进行实际应用。
请关注我,和孤狼一起学习进步。