设计模式-责任链模式

定义

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.

通过给不止一个接收者处理某个请求的机会来避免请求的发送者和接收者之间的耦合。将所有的接受者链起来,然后将请求在链上传递直到有一个接收者去处理它。

之所以首先提到这个模式,是因为我刚开始接触到的有印象的就是责任链模式。而它的用途也比较广泛,举个比较常见的例子,经常会看到一种下面这种代码:

1
2
3
4
5
6
7
8
9
10
//接口列表
if (SystemConfig.ECJSON_FPMXXX_CX.equals(restEntity.getGlobalInfo().getInterfaceCode())) {//发票明细查询
respEntity = restService.fpmxxxcx(content,restEntity);
} else if (SystemConfig.ECJSON_ZXYHSL_CX.equals(restEntity.getGlobalInfo().getInterfaceCode())) {//在线户数查询
respEntity = restService.zxyhcx(restEntity,InterfaceCode);
} else if (SystemConfig.ECJSON_AZKPSL_CX.equals(restEntity.getGlobalInfo().getInterfaceCode())){//安装开票户数
respEntity = restService.azkpyhscx(restEntity,content);
} else if(SystemConfig.ECJSON_YKPJE_CX.equals(restEntity.getGlobalInfo().getInterfaceCode())){//月开票金额
}
//......

后面忽略了很大一部分的if else,这种代码不仅看起头皮发麻,而且耦合高,如果新增新的接口,必然在这个高层需要新增if else,违背了开闭原则,这时候就可以用责任链去优化,不仅灵活而且耦合度更低。

类图和代码

对于这些业务的接收者即不同接口编码的处理者,我们可以抽象出一个AbstractHandler,其中主要的有这几点:对外用于处理请求的processRequire;需要子类具体实现业务的response;子列表明自己处理的业务标志。

这里模拟一个场景:用户随时会提意见,可能是跟产品经理要求增加功能;可能是要求程序员优化代码,可能是要求UI修改一下图片样式等。

类图如下:

设计模式-责任链模式\chain of responsibility

抽象处理父类代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public abstract class AbstractHandler {

public static String CODE_REQUIRE = "code";
public static String UI_REQUIRE = "ui";
public static String FUNC_REQUIRE = "func";
//下一个节点

private AbstractHandler nextHandler;

//请求的业务类型
private String requireType;

public AbstractHandler(String type) {
this.requireType = type;
}

public void setNextHandler(AbstractHandler nextHandler) {
this.nextHandler = nextHandler;
}


//模板方法处理请求,不允许子类重写

public final void processRequire(IRequirement requirement){
if(this.requireType.equals(requirement.getType())) this.response(requirement);
else if (this.nextHandler != null) this.nextHandler.processRequire(requirement);
else System.out.println("无法处理该请求:"+requirement.getRequest());
}
//子类自己实现自己的业务

protected abstract void response(IRequirement requirement);

用户提需求的一个抽象:

1
2
3
4
5
6
7
8
9
10
public interface IRequirement {

//表明过来的需求类型
public String getType();

//需求内容

public String getRequest();

}

然后分别定义几个部门里的技术支持人员:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @Description: 程序员
* @Author: 刘会俊
* @Date: 2019-03-22 13:36
*/
public class Coder extends AbstractHandler {


public Coder() {
super(AbstractHandler.CODE_REQUIRE);
}

@Override
protected void response(IRequirement requirement) {
System.out.println("程序员拿到需求为:"+requirement.getRequest()+",正在处理!");
}
}

其他如产品经理或者UI设计师也类似。

然后模拟用户可能会提的需求种类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @Description: 代码优化需求
* @Author: 刘会俊
* @Date: 2019-03-22 13:51
*/
public class CoderRequirment implements IRequirement {
@Override
public String getType() {
return AbstractHandler.CODE_REQUIRE;
}

@Override
public String getRequest() {
return "优化一下代码!!!";
}
}

其他UI需求,或者产品需求也同上类似。

再建立一个映射类,每次调用只需要在映射类里找到责任链第一个节点,并组装链返回即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MappingHandler{

public static AbstractHandler findHandler(){
AbstractHandler coder = new Coder();
AbstractHandler ui = new Ui();
AbstractHandler product = new Product();

product.setNextHandler(coder);
coder.setNextHandler(ui);
return product;
}

}

在高层的控制层就可以很轻松地调用了,它不需要知道是谁处理的和处理细节,传入参数就能拿到返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* @Description: 控制层
* @Author: 刘会俊
* @Date: 2019-03-22 13:45
*/
public class Controller {

public static void main(String[] args) {

AbstractHandler product = MappingHandler.findHandler();

IRequirement coderRequire = new CoderRequirment();
IRequirement functionRequire = new FunctionRequirement();
IRequirement unkonwnRequire = new IRequirement() {
@Override
public String getType() {
return "unknown";
}

@Override
public String getRequest() {
return "未知请求";
}
};
product.processRequire(coderRequire);
product.processRequire(functionRequire);
product.processRequire(unkonwnRequire);

}

}

打印结果:

程序员拿到需求为:优化一下代码!!!,正在处理!
产品经理获得需求为:增加一个功能!!!,正在处理中
无法处理该请求:未知请求

借助Spring

借助于Spring,我们还可以玩出更炫酷的花样。我们都知道链节点的增多会导致每个请求不同的递归调用、判断会耗费很多的判断时间,但是如果此时假如能有个容器,通过循环容器里的AbstractHandler找到每个子类的处理等级,将处理等级同请求类型比对,成功即可具体操作业务,这样效率会高很多,这时候Spring 的酷炫玩法就来了:

AbstractHandler的代码基本保持一致,但是setNextHandler这个方法就可以不用了,需要新增一个getRequireType来作为模板方法获取当前子类的处理类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class AbstractHandler {

public static String CODE_REQUIRE = "code";
public static String UI_REQUIRE = "ui";
public static String FUNC_REQUIRE = "func";

private AbstractHandler nextHandler;

private String requireType;

public String getRequireType() {
return requireType;
}

public AbstractHandler(String type) {
this.requireType = type;
}

//处理请求
public final void processRequire(IRequirement requirement){
this.response(requirement);
}
//返回响应
protected abstract void response(IRequirement requirement);
}

将所有子类纳入Spring容器的管理,加上注解即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class Coder extends AbstractHandler {


public Coder() {
super(AbstractHandler.CODE_REQUIRE);
}

@Override
protected void response(IRequirement requirement) {
System.out.println("程序员拿到需求为:"+requirement.getRequest()+",正在处理!");
}
}

Product和Ui两个类同理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Component
public class HandlerMapping implements InitializingBean, ApplicationContextAware {
//spring容器上下文

private ApplicationContext applicationContext;
//存放业务处理的容器

List<AbstractHandler> abstractHandlerList = new ArrayList<>();
//spring容器会在加载完毕执行这个方法,在这里会读取到AbstractHandler.class类型的bean的名字数组进行循环,然后从beanfactory里根据类名取出来对应的bean放入业务容器
@Override
public void afterPropertiesSet() throws Exception {
String[] abstractHandlers = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, AbstractHandler.class);
for (String abstractHandlerName : abstractHandlers) {
abstractHandlerList.add((AbstractHandler) applicationContext.getBean(abstractHandlerName));
}
}
//获取spring 容器上下文
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
//映射,循环获取业务逻辑代码,根据代码返回对应的处理器
public AbstractHandler mapping(IRequirement requirement){
if ( abstractHandlerList.size() > 0) {
for (AbstractHandler abstractHandler : abstractHandlerList) {
if (abstractHandler.getRequireType().equals(requirement.getType())){
return abstractHandler;
}
}
}
return null;
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

@Autowired
private HandlerMapping handlerMapping;

@Test
public void contextLoads() {
IRequirement coderRequirment = new CoderRequirment();
AbstractHandler coderHandler = handlerMapping.mapping(coderRequirment);
coderHandler.processRequire(coderRequirment);

IRequirement funcRequirment = new FunctionRequirement();
AbstractHandler funcHandler = handlerMapping.mapping(funcRequirment);
funcHandler.processRequire(funcRequirment);

}

}

执行结果:

程序员拿到需求为:优化一下代码!!!,正在处理!
产品经理获得需求为:增加一个功能!!!,正在处理中

总结

优点:将请求和处理分开,甚至不需要处理细节;扩展方便,以后新增业务只需要新增一个业务类型,接入链即可,结合spring甚至只需要新增业务类,耦合度大大降低;

缺点:链如果太长会导致性能问题;链太长不利于调试。

坚持原创、技术分享。请作者喝杯茶吧!