今天与大家谈一谈责任链模式 (Chain of Responsibility Pattern)。
目的
在发送请求的类和最终处理的类之间进行解耦。
例子代码
小伙伴们都看过 大头儿子小头爸爸 么(大手牵小手, 走路不怕滑~)当初纯洁的我怎么也没想到小头爸爸的绿帽子这么鲜艳...
话说大家都这么说, 大头妈妈天天在家里生气, 小头爸爸内心也忍不住了, 于是要证明自己的清(bei)白(lv)😳
除了王叔叔, 还有二个他很怀疑的对象:尖鼻子厨师和粗眉毛保安大哥。
他现在就想看看这到底是谁的责任,这个时候他采取了如下的代码。
定义一个大头儿子类:
@Getter
publicclassBigHeadSon{
//是否尖鼻子
privatefinalbooleanpointedNose =false;
//是否粗眉毛
privatefinalbooleancoarseEyebrows =false;
//是否大头
privatefinalbooleanbigHead =true;
}
定义一个决定儿子爸爸的类:
publicclassDecideFather{
publicstaticStringfindFatherName
(BigHeadSon son){
if(son.isPointedNose()) {
return"尖鼻子厨师";
}
if(son.isCoarseEyebrows()) {
return"粗眉毛保安";
}
if(son.isBigHead()) {
return"隔壁老王";
}
return"小头爸爸";
}
}
然后小头爸爸试了试:
publicclassApp{
publicstaticvoidmain(String[] args){
System.out.println(DecideFather.
findFatherName(newBigHeadSon()));
}
}
问题分析
很明显, if-else 可能无限的地方就是设计模式可以使用的地方。
这时候比如他又怀疑了卖狗狗的大胡子叔叔, 那他又要加个 if-else 了, 这个函数最终可能上百行, 变量可能共享, 那他就很难受了😫
责任链模式
先定义一套责任链的共有类:
publicinterfaceChainNode
{
R extends AbstractChainNodeRequest>
ChainResultexecute(C context, R request);
}
其中的类都是空实现, chainResult 如下:
@Data
publicclassChainResult
{
privatebooleanprocessingCompleted;
privateT response;
}
定义一个责任链处理器:
publicclassChainProcessor{
publicstatic
AbstractChainNodeResponse>
ThandleChainNodes(List> chainNodes,
AbstractChainNodeContext context,
AbstractChainNodeRequest request,
T defaultValue)
AbstractChainNodeContext context,
AbstractChainNodeRequest request,
T defaultValue){
for(ChainNode chainNode : chainNodes) {
ChainResult execute =
chainNode.execute(context, request);
if(execute.isProcessingCompleted()) {
returnexecute.getResponse();
}
}
returndefaultValue;
}
}
然后我们结合例子代码进行使用:
自定义返回值:
@Data
@NoArgsConstructor
@AllArgsConstructor
publicclassFatherNameChainNodeResponse
extendsAbstractChainNodeResponse
extendsAbstractChainNodeResponse{
//父亲姓名
privateString fatherName;
}
使得大头儿子继承入参类:
@Getter
publicclassBigHeadSon
extendsAbstractChainNodeRequest
extendsAbstractChainNodeRequest{
//是否尖鼻子
privatefinalbooleanpointedNose =false;
//是否粗眉毛
privatefinalbooleancoarseEyebrows =false;
//是否大头
privatefinalbooleanbigHead =true;
}
实现一个抽象的找爸爸类:
publicabstractclassCheckFatherChainNode
implementsChainNode{
@Override
publicChainResult
execute(AbstractChainNodeContext context,
AbstractChainNodeRequest request){
returnnull;
}
}
尖鼻子检查:
publicclassNoseCheckFatherChainNode
extendsCheckFatherChainNode{
@Override
publicChainResult
execute(AbstractChainNodeContext context,
AbstractChainNodeRequest request)
{
BigHeadSon bigHeadSon = (BigHeadSon) request;
ChainResult
chainResult =newChainResult<>();
if(bigHeadSon.isPointedNose()) {
chainResult.setProcessingCompleted(true);
chainResult.setResponse(
newFatherNameChainNodeResponse("尖鼻子厨师"));
}
returnchainResult;
}
}
粗眉毛检查:
publicclassEyeBrowCheckFatherChainNode
extendsCheckFatherChainNode{
@Override
publicChainResult
execute(AbstractChainNodeContext context,
AbstractChainNodeRequest request)
{
BigHeadSon bigHeadSon = (BigHeadSon) request;
ChainResult<FatherNameChainNodeResponse>
chainResult = new ChainResult<>();
if(bigHeadSon.isCoarseEyebrows()) {
chainResult.setProcessingCompleted(true);
chainResult.setResponse(
newFatherNameChainNodeResponse("粗眉毛保安"));
}
returnchainResult;
}
}
大头检查:
publicclassHeadCheckFatherChainNode
extendsCheckFatherChainNode{
@Override
publicChainResult
execute(AbstractChainNodeContext context,
AbstractChainNodeRequest request)
{
BigHeadSon bigHeadSon = (BigHeadSon) request;
ChainResult<FatherNameChainNodeResponse>
chainResult = new ChainResult<>();
if(bigHeadSon.isBigHead()) {
chainResult.setProcessingCompleted(true);
chainResult.setResponse(
newFatherNameChainNodeResponse("隔壁老王"));
}
returnchainResult;
}
}
最终使用:
publicclassApp{
publicstaticvoidmain(String[] args){
System.out.println(ChainProcessor
.handleChainNodes(
Arrays.asList(
newNoseCheckFatherChainNode(),
newEyeBrowCheckFatherChainNode(),
newHeadCheckFatherChainNode()),
null,
newBigHeadSon(),
newFatherNameChainNodeResponse("小头爸爸")));
}
}
一顿操作猛如虎, 一看还是带绿帽......
责任链课后作业
1. 结合 bean 声明学习在 Spring boot 中优雅使用责任链模式
2. 学习使用 Apache Commons Chain 包
3. 作为一个银行开发人员, 我们有个支用请求, 需要验证支用币种是否是CNY(人民币),用户是否在黑名单,是否是实名认证的用户,支用的产品是否可用,支用金额是否充足等, 请使用责任链模式完成
提供了如下入参:
@DatapublicclassLendRequest{
//支用金额
privateString loanAmt;
//支用币种
privateString currency;
//支用产品
privateString pdCode;
//用户唯一标识
privateString userId;
}
提供的接口如下:
public class Client {
//是否不在黑名单
public static boolean isNotInBlack(String userId) {
return true;
}
//是否是实名认证用户
public static boolean isRealUser(String userId) {
return true;
}
//是否产品可用
public static boolean isPdUsable(String pdCode) {
return true;
}
//是否用户额度充足
public static boolean
isHasSoMuchMoney(String userId, String loanAmt) {
return false;
}
}