责任链模式篇章一

场景问题

  1. 申请聚餐费用
    很多公司都有这样的福利,就是项目或者部门向公司申请一些聚餐费用用于组织项目成员或者部门成员进行聚餐活动,以增进人员之间的感情,更有利于工作中的合作.
    申请费用涉及到金额的问题,现在我们假设如下场景:
    如果申请500元以内,项目经理审批即可;
    如果申请超过500元但是少于1000元,部门经理审批即可(项目经理没有权限);
    如果超过1000元,总经理审批即可(部门经理和项目经理都没有权限);-
    //为了测试, 只同意小霞的.方便点
    if ("小霞".equals(user)) {
    result = "[项目]经理同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
    } else {
    result = "[项目]经理[不]同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
    }
    return result;
    }
    private String depManagerHandle(String user, Double fee) {
    String result = "";
    //为了测试, 只同意小霞的.方便点
    if ("小霞".equals(user)) {
    result = "[部门]经理同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
    } else {
    result = "[部门]经理[不]同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
    }
    return result;
    }
    private String generalManagerHandle(String user, Double fee) {
    String result = "";
    //为了测试, 只同意小霞的.方便点
    if ("小霞".equals(user)) {
    result = "[总]经理同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
    } else {
    result = "[总]经理[不]同意" + user + "聚餐费用[" + fee + "]元的申请.";
    }
    return result;
    }
    }

    public class Client {
    public static void main(String[] args) {
    FeeRequest feeRequest = new FeeRequest();

    String user = "小霞";
    String result = feeRequest.feeRequestToProject(user, 300d);
    System.out.println("The result1 = " + result);
    System.out.println();

    String result2 = feeRequest.feeRequestToProject(user, 600d);
    System.out.println("The result2 = " + result2);
    System.out.println();

    String result3 = feeRequest.feeRequestToProject(user, 1600d);
    System.out.println("The result3 = " + result3);
    System.out.println();

    String user2 = "小张";
    String result4 = feeRequest.feeRequestToProject(user2, 300d);
    System.out.println("The result4 = " + result4);
    System.out.println();

    String result5 = feeRequest.feeRequestToProject(user2, 600d);
    System.out.println("The result5 = " + result5);
    System.out.println();

    String result6 = feeRequest.feeRequestToProject(user2, 1600d);
    System.out.println("The result6 = " + result6);

    }
    }
    运行结果:
    The result1 = [项目]经理同意[小霞]聚餐费用[300.0]元的申请.

The result2 = [部门]经理同意[小霞]聚餐费用[600.0]元的申请.

The result3 = [总]经理同意[小霞]聚餐费用[1600.0]元的申请.

The result4 = [项目]经理[不]同意[小张]聚餐费用[300.0]元的申请.

The result5 = [部门]经理[不]同意[小张]聚餐费用[600.0]元的申请.

The result6 = [总]经理[不]同意小张聚餐费用[1600.0]元的申请.

  1. 有什么问题
    问题一: 单纯就上面的ifelse的逻辑就需要考虑优化;
    问题二: 如果处理的逻辑发生了改变, 上面的代码就必须变动,违背了开闭原则;

    把上面的问题抽象化(这个作为设计模式的一个优化的总思路, 先都思考着抽象化);
    1: 抽象角色
    发起请求的角色: request
    处理请求的角色: handler
    2: 引入职责链

解决方案

1: 使用职责链模式来解决问题
2: 职责链的结构和说明
image
a) 抽象处理者角色:定义职责的接口(定义一个处理请求的方法),可以在这里实现后继链组装
b) 具体处理者角色1: 实现职责的类,实现对它职责范围内请求的处理,如果不处理,就转发给后继处理者
c) 具体处理者角色2:同上
d) 客户端角色 : 职责链的客户端,向链上的具体处理对象提交请求,让职责链负责处理.
3: 职责链的示例代码

public abstract class Handler {

//持有后继的职责对象
protected Handler successor;
//设置后继的处理对象
public void setSuccessor(Handler successor) {
this.successor = successor;
}
/**
* 根据具体需要来选择是否传递参数
* 默认还是使用参数
* @param request
*/
public abstract void handleRequest(Object request);
}

public class ConcreteHandler2 extends Handler {

@Override
public void handleRequest(Object request) {
//根据某些条件来判断是否属于自己处理的职责范围
//判断条件比如:从外部传入的参数,或者这里主动去获取的外部数据,
//如从数据库中获取等,下面这句话只是个示意
boolean someCondition = true;
if (someCondition) {
//如果属于自己处理的职责范围,就在这里处理请求

//具体的处理代码
System.out.println("ConcreteHandler2 handle request");
} else {
//如果不属于自己处理的职责范围,那就判断是否还有后继的职责对象
//如果有,就转发请求给后继的职责对象
//如果没有,什么都不做,自然结束
if (this.successor != null) {
this.successor.handleRequest(request);
}
}
}
}

public class ConcreteHandler1 extends Handler {

@Override
public void handleRequest(Object request) {
//根据某些条件来判断是否属于自己处理的职责范围
//判断条件比如:从外部传入的参数,或者这里主动去获取的外部数据,
//如从数据库中获取等,下面这句话只是个示意
boolean someCondition = false;
if (someCondition) {
//如果属于自己处理的职责范围,就在这里处理请求

//具体的处理代码
System.out.println("ConcreteHandler1 handle request");
} else {
if (this.successor != null) {
this.successor.handleRequest(request);
}
}
}
}

public class Client {

public static void main(String[] args) {

//1: 组装职责链, 一般不会放入客户端 todo
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();

handler1.setSuccessor(handler2);

//2: 然后提交请求
handler1.handleRequest(new Object());

}
}

处理结果:
ConcreteHandler2 handle request
或者
ConcreteHandler1 handle request

4: 使用职责链重写案例
4.1 画出UML类图
image
本质: 构建一个链(以抽象类或者接口为起点)
请求(requset)进来->项目经理处理审批者->部门经理审批者->总经理审批者
4.2 代码如下:
public abstract class Handler {

//持有下一个引用
protected Handler successor = null;

//设置下一个处理请求的对象
public void setSuccessor(Handler successor) {
this.successor = successor;
}

//处理请求方法
public abstract String handleFeeRequest(FeeRequest request);
}

@Data
public class FeeRequest {
private String user;
private Double fee;
}

public class ProjectManager extends Handler {

@Override
public String handleFeeRequest(FeeRequest request) {
String result = "";
/*
1: 根据金额判断
2: 根据用户判断 (为了简单的测试)
*/
if (request.getFee() < 500) {
String user = request.getUser();
Double fee = request.getFee();
if ("小霞".equals(request.getUser())) {
result = "[项目]经理同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
} else {
result = "[项目]经理[不]同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
}
} else {
if (this.successor != null) {
result = this.successor.handleFeeRequest(request);
}
}
return result;
}
}

public class DepManager extends Handler {

@Override
public String handleFeeRequest(FeeRequest request) {
String result = "";
/*
1: 根据金额判断
2: 根据用户判断 (为了简单的测试)
*/
if (request.getFee() < 1000) {
String user = request.getUser();
Double fee = request.getFee();
if ("小霞".equals(request.getUser())) {
result = "[部门]经理同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
} else {
result = "[部门]经理[不]同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
}
} else {
if (this.successor != null) {
result = this.successor.handleFeeRequest(request);
}
}
return result;
}
}

public class GeneralManager extends Handler {

@Override
public String handleFeeRequest(FeeRequest request) {
String result = "";
/*
1: 根据金额判断
2: 根据用户判断 (为了简单的测试)
*/
String user = request.getUser();
Double fee = request.getFee();
if ("小霞".equals(request.getUser())) {
result = "[总]经理同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
} else {
result = "[总]经理[不]同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
}
return result;
}
}

public class Client {

public static void main(String[] args) {
//先要组装职责链
Handler h1 = new GeneralManager();
Handler h2 = new DepManager();
Handler h3 = new ProjectManager();

h3.setSuccessor(h2);
h2.setSuccessor(h1);

//开始测试
FeeRequest feeRequest = new FeeRequest();
feeRequest.setFee(300d);
String user = "小霞";
feeRequest.setUser(user);

String result = h3.handleFeeRequest(feeRequest);
System.out.println("The result1 = " + result);
System.out.println();

//600d
feeRequest.setFee(600d);

String result2 = h3.handleFeeRequest(feeRequest);
System.out.println("The result2 = " + result2);
System.out.println();

//1600d
feeRequest.setFee(1600d);
String result3 = h3.handleFeeRequest(feeRequest);
System.out.println("The result3 = " + result3);
System.out.println();

String user2 = "小张";
feeRequest.setUser(user2);
feeRequest.setFee(300d);

String result4 = h3.handleFeeRequest(feeRequest);
System.out.println("The result4 = " + result4);
System.out.println();

feeRequest.setFee(600d);
String result5 = h3.handleFeeRequest(feeRequest);
System.out.println("The result5 = " + result5);
System.out.println();

feeRequest.setFee(1600d);
String result6 = h3.handleFeeRequest(feeRequest);
System.out.println("The result6 = " + result6);
}
}
测试结果:
The result1 = [项目]经理同意[小霞]聚餐费用[300.0]元的申请.

The result2 = [部门]经理同意[小霞]聚餐费用[600.0]元的申请.

The result3 = [总]经理同意[小霞]聚餐费用[1600.0]元的申请.

The result4 = [项目]经理[不]同意[小张]聚餐费用[300.0]元的申请.

The result5 = [部门]经理[不]同意[小张]聚餐费用[600.0]元的申请.

The result6 = [总]经理[不]同意[小张]聚餐费用[1600.0]元的申请.

模式详解

1: 职责链的功能
职责链模式主要用来处理客户端发出的一个请求,多个对象都有机会来处理这个一个请求,但是客户端并不知道到底谁来处理他的请求.这样的,就是需要让请求者和接收者解耦,这样就可以动态切换和组合接收者了.
标准的职责链:只要有对象处理了请求,这个请求就到此为止,不再传递和处理了.
变形的职责链:这个通常用在web方向比较多,这个职责链是让每一个职责对象对请求进行一定的功能处理,从而形成了一个处理请求的功能链.(例如servlet中的filter过滤器)
2: 隐式接收者
对于客户端来说,他并不知道谁处理了请求,对于客户端来说,处理了请求的对象就是隐式接收请求的处理者.
3: 如何构建链
①在客户端提交请求前组合链
②在handler内部组合链
③由各个职责对象自行决定后续的处理对象
构建链的数据来源
①在程序中动态组合
②也可以通过外部,通过数据库来获取组合的方式
③通过配置文件传递
④自行构建链的情况,就是结合组合模式(重点)
4: 谁来处理
这个是运行时期动态决定的.
5: 请求一定会被处理吗?
请求不一定会被处理.

扩展

如果要处理多种请求

前面的职责链都是处理一种请求的形式,现在有这样的需求,还是费用申请的情况,不过现在是差旅费申请,假设还是同一个流程,也就是组合同一个职责链.虽然流程相同,但是每个处理链需要处理两种请求,它们的具体业务逻辑是不一样的,那么该如何实现呢?
1: 简单的处理方式
直接在原来的代码上面新增处理差旅费的抽象方法,然后各个子类进行实现,再进行调用,这样确实会存在问题。
贴出部分代码:

public abstract class Handler {

//持有下一个引用
protected Handler successor = null;

//设置下一个处理请求的对象
public void setSuccessor(Handler successor) {
this.successor = successor;
}

//处理请求方法
public abstract String handleFeeRequest(FeeRequest request);

//todo 扩展: 新增差旅费申请
public abstract String handleTravelExpenseRequest(FeeRequest request);
}

public class ProjectManager extends Handler {

@Override
public String handleFeeRequest(FeeRequest request) {
String result = "";
/*
1: 根据金额判断
2: 根据用户判断 (为了简单的测试)
*/
if (request.getFee() < 500) {
String user = request.getUser();
Double fee = request.getFee();
if ("小霞".equals(request.getUser())) {
result = "[项目]经理同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
} else {
result = "[项目]经理[不]同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
}
} else {
if (this.successor != null) {
result = this.successor.handleFeeRequest(request);
}
}
return result;
}

/**
* 差旅费 处理
* @param request
* @return
/
@Override
public String handleTravelExpenseRequest(FeeRequest request) {
String result = "";
/

1: 根据金额判断
2: 根据用户判断 (为了简单的测试)
*/
if (request.getFee() < 5000) {
String user = request.getUser();
Double fee = request.getFee();
if ("小霞".equals(request.getUser())) {
result = "[项目]经理同意[" + user + "]预支差旅费用[" + fee + "]元的申请.";
} else {
result = "[项目]经理[不]同意[" + user + "]预支差旅费用[" + fee + "]元的申请.";
}
} else {
if (this.successor != null) {
result = this.successor.handleTravelExpenseRequest(request);
}
}
return result;
}
}
其他的类似,就不贴出了。

2: 通用请求的处理方式
上面的实现方式是有很大问题的,只要增加一个业务我们就需要修改职责的接口,这是太不灵活了.
刚刚的改动基本上是全部的类都动到了.
比较好的实现是: 当我们增加一个业务的时候,不需要动接口(接口一般是不动的, 因为接口一动,实现也会跟着动)
分析一下变化:(根据变化来设计(设计的具体原则))
2.1 不同的业务需要传递的业务数据不同
2.2 不同的业务请求的方法不同
2.3 不同的职责对象处理这些不同的业务请求的业务逻辑不同
通常的解决思路, 都是考虑通用调用框架
实现通用的原则:
①请求参数使用Object(可以具体化就使用具体的)
②响应参数使用Object(可以具体化就使用具体的)
③存在的不同的,就使用一个标志进行区分即可(业务标记)
④使用通用的处理方法

public abstract class Handler4 {

protected Handler4 successor;

//属于构造链方法
public void setSuccessor(Handler4 successor) {
this.successor = successor;
}

/**
*
* @param request
* @return
*/
public Object handleRequest(FeeRequest4 request) {
if (this.successor != null) {
//这个是默认的实现,如果子类不愿意处理这个请求,那就传递到下一个职责对象去处理
return this.successor.handleRequest(request);
} else {
System.out.println("没有后续处理或者暂时不支持这样的功能处理");
return false;
}
}

/**
* 内部建造者
*/
public static class Builder {
Handler4 head;
Handler4 tail;
// 根本不需要构造 多余的开放的方法 可能会有问题
// public Builder(Handler4 handler4) {
//
// }
public Builder addHandler4(Handler4 handler4) {
if (this.head == null) {
this.head = this.tail = handler4;
return this;
}
this.tail.setSuccessor(handler4);
this.tail = handler4;
return this;
}

public Handler4 build() {
return this.head;
}
}
}

public class ProjectManager4 extends Handler4 {

/**
*
* @param request
* @return
*/
@Override
public Object handleRequest(FeeRequest4 request) {
if (FeeRequestModel.FEE_TYPE.equalsIgnoreCase(request.getType())) {
return handleFeeRequest(request);
}
//如果不在自己的职责范围 直接转发给下一个
return super.handleRequest(request);
}

/**
* 在当前处理角色的职责之下就进行处理
* @param request
* @return
*/
private Object handleFeeRequest(FeeRequest4 request) {

//先把通用的对象造型回来
FeeRequestModel feeRequestModel = (FeeRequestModel) request;
String result = "";
if (feeRequestModel.getFee() < 500) {
String user = feeRequestModel.getUser();
Double fee = feeRequestModel.getFee();
if ("小霞".equals(feeRequestModel.getUser())) {
result = "[项目]经理同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
} else {
result = "[项目]经理[不]同意[" + user + "]聚餐费用[" + fee + "]元的申请.";
}
} else {
if (this.successor != null) {
return this.successor.handleRequest(feeRequestModel);
}
}
return result;
}
}

@Data
public class FeeRequest4 {
private String user;
private Double fee;
//表示具体的业务类型
private String type;

public FeeRequest4(String type) {
this.type = type;
}
}

@Data
public class FeeRequestModel extends FeeRequest4 {

public static final String FEE_TYPE = "fee";

public FeeRequestModel() {
super(FEE_TYPE);
}

}

@Data
public class PreFeeRequestModel extends FeeRequest4 {

public static final String FEE_TYPE = "preFee";

public PreFeeRequestModel() {
super(FEE_TYPE);
}
}
image
本质还是通过继承增强了项目经理的职责。

功能链

职责链模式的优缺点

思考职责链模式

相关模式

结合使用

写前准备

责任链模式:拦截器模式:Pipeline模式:流水线模式:过滤器
①申请聚餐费用的管理
如果申请
1000 部门经理
2000 总经理
3000 副总裁
②tomcat的请求流程, 经过过滤器, pipeline(分析)
③netty的pipeline(分析)
④古代女子向男子申请逛街消费啥的(古代地位不一样)(手写)
⑤责任链模式和建造者模式结合使用(重点)
责任链需要构建链表
然而建造者模式是构建链表的很好的选择
⑤mybatis中的责任链与代理模式综合使用(重点)
⑥责任链与模板模式(重点)
⑦多线程改造->责任链
⑧信访部门的访问过程, 直到有部门来接受信访
⑨责任链+模板完成一个引擎(重点)

【参考】研磨设计模式

posted @ 2021-04-15 16:21  oldsmile  阅读(75)  评论(0)    收藏  举报