#0 系列目录#数据库
#1 场景问题# ##1.1 申请聚餐费用## 来考虑这样一个功能:申请聚餐费用的管理。编程
不少公司都有这样的福利,就是项目组或者是部门能够向公司申请一些聚餐费用,用于组织项目组成员或者是部门成员进行聚餐活动,以增进人员之间的情感,更有利于工做中的相互合做。设计模式
申请聚餐费用的大体流程通常是:由申请人先填写申请单,而后交给领导审查,若是申请批准下来了,领导会通知申请人审批经过,而后申请人去财务核领费用,若是没有核准,领导会通知申请人审批未经过,此事也就此做罢了。安全
不一样级别的领导,对于审批的额度是不同的,好比:项目经理只能审批500元之内的申请;部门经理能审批1000元之内的申请;而总经理能够审核任意额度的申请。框架
也就是说,当某人提出聚餐费用申请的请求后,该请求会由项目经理、部门经理、总经理之中的某一位领导来进行相应的处理,可是提出申请的人并不知道最终会由谁来处理他的请求,通常申请人是把本身的申请提交给项目经理,或许最后是由总经理来处理他的请求,可是申请人并不知道应该由总经理来处理他的申请请求。测试
那么该怎样实现这样的功能呢?ui
##1.2 不用模式的解决方案## 分析上面要实现的功能,主要就是要根据申请费用的多少,而后让不一样的领导来进行处理就能够实现了。也就是有点逻辑判断而已,示例代码以下:this
/** * 处理聚餐费用申请的对象 */ public class FeeRequest { /** * 提交聚餐费用申请给项目经理 * @param user 申请人 * @param fee 申请费用 * @return 成功或失败的具体通知 */ public String requestToProjectManager(String user,double fee){ String str = ""; if(fee < 500){ //项目经理的权限比较小,只能在500之内 str = this.projectHandle(user, fee); }else if(fee < 1000){ //部门经理的权限只能在1000之内 str = this.depManagerHandle(user, fee); }else if(fee >= 1000){ //总经理的权限很大,只要请求到了这里,他均可以处理 str = this.generalManagerHandle(user, fee); } return str; } /** * 项目经理审批费用申请,参数、返回值和上面是同样的,省略了 */ private String projectHandle(String user, double fee) { String str = ""; //为了测试,简单点,只赞成小李的 if("小李".equals(user)){ str = "项目经理赞成"+user+"聚餐费用"+fee+"元的请求"; }else{ //其它人一概不一样意 str = "项目经理不一样意"+user+"聚餐费用"+fee+"元的请求"; } return str; } /** * 部门经理审批费用申请,参数、返回值和上面是同样的,省略了 */ private String depManagerHandle(String user, double fee) { String str = ""; //为了测试,简单点,只赞成小李申请的 if("小李".equals(user)){ str = "部门经理赞成"+user+"聚餐费用"+fee+"元的请求"; }else{ //其它人一概不一样意 str= "部门经理不一样意"+user+"聚餐费用"+fee+"元的请求"; } return str; } /** * 总经理审批费用申请,参数、返回值和上面是同样的,省略了 */ private String generalManagerHandle(String user, double fee) { String str = ""; //为了测试,简单点,只赞成小李的 if("小李".equals(user)){ str = "总经理赞成"+user+"聚餐费用"+fee+"元的请求"; }else{ //其它人一概不一样意 str = "总经理不一样意"+user+"聚餐费用"+fee+"元的请求"; } return str; } }
写个客户端来测试看看效果,示例代码以下:.net
public class Client { public static void main(String[] args) { FeeRequest request = new FeeRequest(); //开始测试 String ret1 = request.requestToProjectManager("小李", 300); System.out.println("the ret="+ret1); String ret2 = request.requestToProjectManager("小张", 300); System.out.println("the ret="+ret2); String ret3 = request.requestToProjectManager("小李", 600); System.out.println("the ret="+ret3); String ret4 = request.requestToProjectManager("小张", 600); System.out.println("the ret="+ret4); String ret5 = request.requestToProjectManager("小李", 1200); System.out.println("the ret="+ret5); String ret6 = request.requestToProjectManager("小张", 1200); System.out.println("the ret="+ret6); } }
运行结果以下:设计
the ret1=项目经理赞成小李聚餐费用300.0元的请求 the ret2=项目经理不一样意小张聚餐费用300.0元的请求 the ret3=部门经理赞成小李聚餐费用600.0元的请求 the ret4=部门经理不一样意小张聚餐费用600.0元的请求 the ret5=总经理赞成小李聚餐费用1200.0元的请求 the ret6=总经理不一样意小张聚餐费用1200.0元的请求
##1.3 有何问题## 上面的实现很简单,基本上没有什么特别的难度。仔细想一想,这么实现有没有问题呢?仔细分析申请聚餐费用的业务功能和目前的实现,主要面临着以下问题:
聚餐费用申请的处理流程是可能会变更的。
好比如今的处理流程是:提交申请给项目经理,看看是否适合由项目经理处理,若是不是,看看是否适合由部门经理处理,若是不是,看看是否适合由总经理处理的步骤。从此可能会变化成:直接提交给部门经理,看看是否适合由部门经理处理,若是不是,总经理处理这样的步骤。也就是说,对于聚餐费用申请,要求处理的逻辑步骤是灵活的。
各个处理环节的业务处理也是会变更的。
由于处理流程可能发生变化,也会致使某些步骤的具体的业务功能发生变化,好比:本来部门经理审批聚餐费用的时候,只是判断是否批准;如今,部门经理可能在审批聚餐费用的时候,核算本部门的实时成本,这就出现新的业务处理功能了。
若是采用上面的实现,要是处理的逻辑发生了变化,解决的方法,一个是生成一个子类,覆盖requestToProjectManager方法,而后在里面实现新的处理
;另一个方法就是修改处理申请的方法的源代码来实现
。要是具体处理环节的业务处理的功能发生了变化,那就只好找到相应的处理方法,进行源代码修改了
。
总之都不是什么好方法,也就是说,若是出现聚餐费用申请的处理流程变化的状况,或者是出现各个处理环节的功能变化的时候,上面的实现方式是很难灵活的变化来适应新功能的要求的。
把上面的问题抽象一下:客户端发出一个请求,会有不少对象均可以来处理这个请求,并且不一样对象的处理逻辑是不同的
。对于客户端而言,无所谓谁来处理,反正有对象处理就能够了。
并且在上述处理中,还但愿处理流程是能够灵活变更的,而处理请求的对象须要能方便的修改或者是被替换掉,以适应新的业务功能的须要。
请问如何才能实现上述要求?
#2 解决方案# ##2.1 职责链模式来解决## 用来解决上述问题的一个合理的解决方案,就是使用职责链模式。那么什么是职责链模式呢?
仔细分析上面的场景,当客户端提出一个聚餐费用的申请,后续处理这个申请的对象,项目经理、部门经理和总经理,天然的造成了一个链,从项目经理-部门经理-总经理,客户端的申请请求就在这个链中传递,直到有领导处理为止。看起来,上面的功能要求很适合采用职责链来处理这个业务。
要想让处理请求的流程能够灵活的变更,一个基本的思路,那就是动态构建流程步骤
,这样随时均可以从新组合出新的流程来。而要让处理请求的对象也要很灵活,那就要让它足够简单,最好是只实现单一的功能,或者是有限的功能,这样更有利于修改和复用
。
职责链模式就很好的体现了上述的基本思路,首先职责链模式会定义一个全部处理请求的对象都要继承实现的抽象类
,这样就有利于随时切换新的实现;其次每一个处理请求对象只实现业务流程中的一步业务处理
,这样使其变得简单;最后职责链模式会动态的来组合这些处理请求的对象
,把它们按照流程动态组合起来,并要求它们依次调用,这样就动态的实现了流程。
这样一来,若是流程发生了变化,只要从新组合就行了
;若是某个处理的业务功能发生了变化,一个方案是修改该处理对应的处理对象,另外一个方案是直接提供一个新的实现,而后在组合流程的时候,用新的实现替换掉旧的实现就能够了。
##2.2 模式结构和说明## 职责链模式的结构如图23.1所示:
Handler:定义职责的接口
,一般在这里定义处理请求的方法,能够在这里实现后继链。
ConcreteHandler:实现职责的类
,在这个类里面,实现对在它职责范围内请求的处理,若是不处理,就继续转发请求给后继者。
Client:职责链的客户端
,向链上的具体处理者对象提交请求,让职责链负责处理。
##2.3 职责链模式示例代码##
/** * 职责的接口,也就是处理请求的接口 */ public abstract class Handler { /** * 持有后继的职责对象 */ protected Handler successor; /** * 设置后继的职责对象 * @param successor 后继的职责对象 */ public void setSuccessor(Handler successor) { this.successor = successor; } /** * 示意处理请求的方法,虽然这个示意方法是没有传入参数, * 但实际是能够传入参数的,根据具体须要来选择是否传递参数 */ public abstract void handleRequest(); }
/** * 具体的职责对象,用来处理请求 */ public class ConcreteHandler1 extends Handler { public void handleRequest() { //根据某些条件来判断是否属于本身处理的职责范围 //判断条件好比:从外部传入的参数,或者这里主动去获取的外部数据, //如从数据库中获取等,下面这句话只是个示意 boolean someCondition = false; if(someCondition){ //若是属于本身处理的职责范围,就在这里处理请求 //具体的处理代码 System.out.println("ConcreteHandler1 handle request"); }else{ //若是不属于本身处理的职责范围,那就判断是否还有后继的职责对象 //若是有,就转发请求给后继的职责对象 //若是没有,什么都不作,天然结束 if(this.successor!=null){ this.successor.handleRequest(); } } } }
另一个ConcreteHandler2和上面ConcreteHandler1的示意代码几乎是同样的,所以就不去赘述了。
/** * 职责链的客户端,这里只是个示意 */ public class Client { public static void main(String[] args) { //先要组装职责链 Handler h1 = new ConcreteHandler1(); Handler h2 = new ConcreteHandler2(); h1.setSuccessor(h2); //而后提交请求 h1.handleRequest(); } }
##2.4 使用职责链模式重写示例## 要使用职责链模式来重写示例,仍是先来实现以下的功能:当某人提出聚餐费用申请的请求后,该请求会在项目经理-部门经理-总经理这样一条领导处理链上进行传递,发出请求的人并不知道谁会来处理他的请求,每一个领导会根据本身的职责范围,来判断是处理请求仍是把请求交给更高级的领导,只要有领导处理了,传递就结束了
。
须要把每位领导的处理独立出来,实现成单独的职责处理对象,而后为它们提供一个公共的、抽象的父职责对象,这样就能够在客户端来动态的组合职责链,实现不一样的功能要求了
。仍是看一下示例的总体结构,会有助于对示例的理解和把握。如图23.2所示:
首先来看看定义全部职责的抽象类,也就是全部职责的外观,在这个类里面持有下一个处理请求的对象,同时还要定义业务处理方法,示例代码以下:
/** * 定义职责对象的接口 */ public abstract class Handler { /** * 持有下一个处理请求的对象 */ protected Handler successor = null; /** * 设置下一个处理请求的对象 * @param successor 下一个处理请求的对象 */ public void setSuccessor(Handler successor){ this.successor = successor; } /** * 处理聚餐费用的申请 * @param user 申请人 * @param fee 申请的钱数 * @return 成功或失败的具体通知 */ public abstract String handleFeeRequest(String user,double fee); }
如今实现的处理聚餐费用流程是:申请人提出的申请交给项目经理处理,项目经理的处理权限是500元之内,超过500元,把申请转给部门经理处理,部门经理的处理权限是1000元之内,超过1000元,把申请转给总经理处理。
分析上述流程,对请求主要有三个处理环节,把它们分别实现成为职责对象,一个对象实现一个环节的处理功能
,这样就会比较简单。
先看看项目经理的处理吧,示例代码以下:
public class ProjectManager extends Handler{ public String handleFeeRequest(String user, double fee) { String str = ""; //项目经理的权限比较小,只能在500之内 if(fee < 500){ //为了测试,简单点,只赞成小李的 if("小李".equals(user)){ str = "项目经理赞成"+user+"聚餐费用"+fee+"元的请求"; }else{ //其它人一概不一样意 str = "项目经理不一样意"+user+"聚餐费用"+fee+"元的请求"; } return str; }else{ //超过500,继续传递给级别更高的人处理 if(this.successor!=null){ return successor.handleFeeRequest(user, fee); } } return str; } }
接下来看看部门经理的处理,示例代码以下:
public class DepManager extends Handler{ public String handleFeeRequest(String user, double fee) { String str = ""; //部门经理的权限只能在1000之内 if(fee < 1000){ //为了测试,简单点,只赞成小李申请的 if("小李".equals(user)){ str = "部门经理赞成"+user+"聚餐费用"+fee+"元的请求"; }else{ //其它人一概不一样意 str = "部门经理不一样意"+user+"聚餐费用"+fee+"元的请求"; } return str; }else{ //超过1000,继续传递给级别更高的人处理 if(this.successor!=null){ return this.successor.handleFeeRequest(user, fee); } } return str; } }
再看总经理的处理,示例代码以下:
public class GeneralManager extends Handler{ public String handleFeeRequest(String user, double fee) { String str = ""; //总经理的权限很大,只要请求到了这里,他均可以处理 if(fee >= 1000){ //为了测试,简单点,只赞成小李的 if("小李".equals(user)){ str = "总经理赞成"+user+"聚餐费用"+fee+"元的请求"; }else{ //其它人一概不一样意 str = "总经理不一样意"+user+"聚餐费用"+fee+"元的请求"; } return str; }else{ //若是还有后继的处理对象,继续传递 if(this.successor!=null){ return successor.handleFeeRequest(user, fee); } } return str; } }
那么客户端如何使用职责链呢,最重要的就是要先构建职责链,而后才能使用。示例代码以下:
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); //开始测试 String ret1 = h3.handleFeeRequest("小李", 300); System.out.println("the ret1="+ret1); String ret2 = h3.handleFeeRequest("小张", 300); System.out.println("the ret2="+ret2); String ret3 = h3.handleFeeRequest("小李", 600); System.out.println("the ret3="+ret3); String ret4 = h3.handleFeeRequest("小张", 600); System.out.println("the ret4="+ret4); String ret5 = h3.handleFeeRequest("小李", 1200); System.out.println("the ret5="+ret5); String ret6 = h3.handleFeeRequest("小张", 1200); System.out.println("the ret6="+ret6); } }
运行结果以下:
the ret1=项目经理赞成小李聚餐费用300.0元的请求 the ret2=项目经理不一样意小张聚餐费用300.0元的请求 the ret3=部门经理赞成小李聚餐费用600.0元的请求 the ret4=部门经理不一样意小张聚餐费用600.0元的请求 the ret5=总经理赞成小李聚餐费用1200.0元的请求 the ret6=总经理不一样意小张聚餐费用1200.0元的请求
看起来结果跟前面不用模式的实现方案的运行结果是同样的,它们原本就是实现的一样的功能,只不过实现方式不一样而已。
理解了示例的总体结构和具体实现,那么示例的具体运行过程是怎样的呢?
下面就以“小李申请聚餐费用1200元”这个费用申请为例来讲明,调用过程的示意图如图23.3所示:
#3 模式讲解# ##3.1 认识职责链模式##
职责链模式主要用来处理:“客户端发出一个请求,有多个对象都有机会来处理这一个请求,可是客户端不知道究竟谁会来处理他的请求”
,这样的状况。也就是须要让请求者和接收者解耦,这样就能够动态的切换和组合接收者了
。
要注意在标准的职责链模式里面,是只要有对象处理了请求,这个请求就到此为止,再也不被传递和处理了。
若是是要变形使用职责链,就可让这个请求继续传递,每一个职责对象对这个请求进行必定的功能处理,从而造成一个处理请求的功能链。
当客户端发出请求的时候,客户端并不知道谁会真正处理他的请求,客户端只知道他提交请求的第一个对象。从第一个处理对象开始,整个职责链里面的对象,要么本身处理请求,要么继续转发给下一个接收者。
也就是对于请求者而言,并不知道最终的接收者是谁,可是通常状况下,老是会有一个对象来处理的,所以称为隐式接收者
。
职责链的链怎么构建呢?这是个大问题,实现的方式也是五花八门,归结起来大体有如下一些方式。
首先是按照实现的地方来讲:
能够实如今客户端,
在提交请求前组合链
,也就是在使用的时候动态组合链,称为外部链;也能够在Handler里面实现链的组合,算是内部链的一种;
固然还有一种就是在各个职责对象里面,
由各个职责对象自行决定后续的处理对象
,这种实现方式要求每一个职责对象除了进行业务处理外,还必须了解整个业务流程。
按照构建链的数据来源,也就是决定了按照什么顺序来组合链的数据,又分为几种:
一种就是在程序里面动态组合;
也能够经过外部,好比数据库来获取组合的数据,这种属于数据库驱动的方式;
还有一种方式就是经过配置文件传递进来,也能够是流程的配置文件;
若是是从外部获取数据来构建链,那么在程序运行的时候,会读取这些数据,而后根据数据的要求来获取相应的对象,并组合起来。
还有一种是不须要构建链,由于已有的对象已经天然构成链了,这种状况多出如今组合模式构建的对象树中,这样子对象能够很天然的向上找到本身的父对象
。就像部门人员的组织结构同样,顶层是总经理,总经理下面是各个部门的经理,部门经理下面是项目经理,项目经理下面是各个普通员工,天然就能够造成:普通员工-项目经理-部门经理-总经理这样的链。
职责链中那么多处理对象,到底谁来处理请求呢,这个是在运行时期动态决定的
。当请求被传递到某个处理对象的时候,这个对象会按照已经设定好的条件来判断,是否属于本身处理的范围,若是是就处理,若是不是就转发请求给下一个对象。
在职责链模式中,请求不必定会被处理,由于可能没有合适的处理者,请求在职责链里面从头传递到尾,每一个处理对象都判断不属于本身处理,最后请求就没有对象来处理。这一点是须要注意的。
能够在职责链的末端始终加上一个不支持此功能处理的职责对象,这样若是传递到这里,就会出现提示,本职责链没有对象处理这个请求。
##3.2 处理多种请求## 前面的示例都是同一个职责链处理一种请求的状况
,如今有这样的需求,仍是费用申请的功能,此次是申请预支差旅费,假设仍是同一流程,也就是组合同一个职责链,从项目经理-传递给部门经理-传递给总经理,虽然流程相同,可是每一个处理类须要处理两种请求,它们的具体业务逻辑是不同的
,那么该如何实现呢?
要解决这个问题,也不是很困难,一个简单的方法就是为每种业务单独定义一个方法,而后客户端根据不一样的须要调用不一样的方法
,仍是经过代码来示例一下。注意这里故意的把两个方法作的有些不同,一个是返回String类型的值,一个是返回boolean类型的值
;另一个是返回到客户端再输出信息,一个是直接在职责处理里面就输出信息
。
(1)首先是改造职责对象的接口,添加上新的业务方法,示例代码以下:
/** * 定义职责对象的接口 */ public abstract class Handler { /** * 持有下一个处理请求的对象 */ protected Handler successor = null; /** * 设置下一个处理请求的对象 * @param successor 下一个处理请求的对象 */ public void setSuccessor(Handler successor){ this.successor = successor; } /** * 处理聚餐费用的申请 * @param user 申请人 * @param fee 申请的钱数 * @return 成功或失败的具体通知 */ public abstract String handleFeeRequest(String user,double fee); /** * 处理预支差旅费用的申请 * @param user 申请人 * @param requestFee 申请的钱数 * @return 是否赞成 */ public abstract boolean handlePreFeeRequest(String user,double requestFee); }
(2)职责的接口发生了改变,对应的处理类也要改变,这几个处理类是相似的,原有的功能不变,而后在新的实现方法里面,一样判断一下是否属于本身处理的范围,若是属于本身处理的范围那就处理,不然就传递到下一个处理。仍是示范一个,看看项目经理的处理吧,示例代码以下:
public class ProjectManager extends Handler{ public String handleFeeRequest(String user, double fee) { String str = ""; //项目经理的权限比较小,只能在500之内 if(fee < 500){ //为了测试,简单点,只赞成小李的 if("小李".equals(user)){ str = "项目经理赞成"+user+"聚餐费用"+fee+"元的请求"; }else{ //其它人一概不一样意 str = "项目经理不一样意"+user+"聚餐费用"+fee+"元的请求"; } return str; }else{ //超过500,继续传递给级别更高的人处理 if(this.successor!=null){ return successor.handleFeeRequest(user, fee); } } return str; } public boolean handlePreFeeRequest(String user, double requestNum) { //项目经理的权限比较小,只能在5000之内 if(requestNum < 5000){ //工做须要嘛,通通赞成 System.out.println("项目经理赞成"+user+"预支差旅费用"+requestNum+"元的请求"); return true; }else{ //超过5000,继续传递给级别更高的人处理 if(this.successor!=null){ return this.successor.handlePreFeeRequest(user, requestNum); } } return false; } }
其它的处理相似,就不去演示了。
(3)准备好了各个处理职责的类,看看客户端如何调用,示例代码以下:
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); //开始测试申请聚餐费用 String ret1 = h3.handleFeeRequest("小李", 300); System.out.println("the ret1="+ret1); String ret2 = h3.handleFeeRequest("小李", 600); System.out.println("the ret2="+ret2); String ret3 = h3.handleFeeRequest("小李", 1200); System.out.println("the ret3="+ret3); //开始测试申请差旅费用 h3.handlePreFeeRequest("小张", 3000); h3.handlePreFeeRequest("小张", 6000); h3.handlePreFeeRequest("小张", 32000); } }
运行的结果以下:
the ret1=项目经理赞成小李聚餐费用300.0元的请求 the ret2=部门经理赞成小李聚餐费用600.0元的请求 the ret3=总经理赞成小李聚餐费用1200.0元的请求 项目经理赞成小张预支差旅费用3000.0元的请求 部门经理赞成小张预支差旅费用6000.0元的请求 总经理赞成小张预支差旅费用32000.0元的请求
上面的实现看起来很容易,可是仔细想一想,这样实现有没有什么问题呢?
这种实现方式有一个很明显的问题,那就是只要增长一个业务,就须要修改职责的接口,这是很不灵活的,Java开发中很强调面向接口编程,所以接口应该相对保持稳定,接口一改,须要修改的地方就太多了,频繁修改接口绝对不是个好主意
。
那有没有什么好方法来实现呢?分析一下如今变化的东西:
一是不一样的业务须要传递的业务数据不一样;
二是不一样的业务请求的方法不一样;
三是不一样的职责对象处理这些不一样的业务请求的业务逻辑不一样;
如今有一种简单的方式,能够较好的解决这些问题。首先定义一套通用的调用框架,用一个通用的请求对象来封装请求传递的参数;而后定义一个通用的调用方法,这个方法不去区分具体业务,全部的业务都是这一个方法,那么具体的业务如何区分呢,就是在通用的请求对象里面会有一个业务的标记;到了职责对象里面,愿意处理就跟原来同样的处理方式,若是不肯意处理,就传递到下一个处理对象就行了
。
对于返回值也能够来个通用的,最简单的就是使用Object类型
。
看例子吧,为了示范,先就假定只有一个业务方法,等把这一个方法搞定了,明白了,而后再扩展一个业务方法,就能清晰地看出这种设计的好处了。
(1)先看看通用的请求对象的定义,示例代码以下:
/** * 通用的请求对象 */ public class RequestModel { /** * 表示具体的业务类型 */ private String type; /** * 经过构造方法把具体的业务类型传递进来 * @param type 具体的业务类型 */ public RequestModel(String type){ this.type = type; } public String getType() { return type; } }
(2)看看此时的通用职责处理对象,在这里要实现一个通用的调用框架,示例代码以下:
/** * 定义职责对象的接口 */ public abstract class Handler { /** * 持有下一个处理请求的对象 */ protected Handler successor = null; /** * 设置下一个处理请求的对象 * @param successor 下一个处理请求的对象 */ public void setSuccessor(Handler successor){ this.successor = successor; } /** * 通用的请求处理方法 * @param rm 通用的请求对象 * @return 处理后须要返回的对象 */ public Object handleRequest(RequestModel rm){ if(successor != null){ //这个是默认的实现,若是子类不肯意处理这个请求, //那就传递到下一个职责对象去处理 return this.successor.handleRequest(rm); }else{ System.out.println("没有后续处理或者暂时不支持这样的功能处理"); return false; } } }
(3)如今来加上第一个业务,就是“聚餐费用申请”的处理,为了描述具体的业务数据,须要扩展通用的请求对象,把业务数据封装进去
,另外定义一个请求对象,示例代码以下:
/** * 封装跟聚餐费用申请业务相关的请求数据 */ public class FeeRequestModel extends RequestModel{ /** * 约定具体的业务类型 */ public final static String FEE_TYPE = "fee"; public FeeRequestModel() { super(FEE_TYPE); } /** * 申请人 */ private String user; /** * 申请金额 */ private double fee; public String getUser() { return user; } public void setUser(String user) { this.user = user; } public double getFee() { return fee; } public void setFee(double fee) { this.fee = fee; } }
(4)接下来该实现职责对象的处理了,大同小异的,仍是看一个就好了,看看项目经理的处理吧,在这个处理类里面,首先要覆盖父类的通用业务处理方法,而后在里面处理本身想要实现的业务,不想处理的就让父类去处理,父类会默认的传递给下一个处理对象
,示例代码以下:
/** * 实现项目经理处理聚餐费用申请的对象 */ public class ProjectManager extends Handler{ public Object handleRequest(RequestModel rm){ if(FeeRequestModel.FEE_TYPE.equals(rm.getType())){ //表示聚餐费用申请 return handleFeeRequest(rm); }else{ //其它的项目经理暂时不想处理 return super.handleRequest(rm); } } private Object handleFeeRequest(RequestModel rm) { //先把通用的对象造型回来 FeeRequestModel frm = (FeeRequestModel)rm; String str = ""; //项目经理的权限比较小,只能在500之内 if(frm.getFee() < 500){ //为了测试,简单点,只赞成小李的 if("小李".equals(frm.getUser())){ str = "项目经理赞成"+frm.getUser()+"聚餐费用"+frm.getFee()+"元的请求"; }else{ //其它人一概不一样意 str = "项目经理不一样意"+frm.getUser()+"聚餐费用"+frm.getFee()+"元的请求"; } return str; }else{ //超过500,继续传递给级别更高的人处理 if(this.successor!=null){ return successor.handleRequest(rm); } } return str; } }
部门经理、总经理的处理对象和项目经理的处理相似,就不去示例了。
(5)客户端也须要变化,对于客户端,惟一的麻烦是须要知道每一个业务对应的具体的请求对象,由于要封装业务数据进去,示例代码以下:
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); //开始测试申请聚餐费用 FeeRequestModel frm = new FeeRequestModel(); frm.setFee(300); frm.setUser("小李"); //调用处理 String ret1 = (String)h3.handleRequest(frm); System.out.println("ret1="+ret1); //从新设置申请金额,再调用处理 frm.setFee(800); h3.handleRequest(frm); String ret2 = (String)h3.handleRequest(frm); System.out.println("ret2="+ret2); //从新设置申请金额,再调用处理 frm.setFee(1600); h3.handleRequest(frm); String ret3 = (String)h3.handleRequest(frm); System.out.println("ret3="+ret3); } }
运行结果以下:
ret1=项目经理赞成小李聚餐费用300.0元的请求 ret2=部门经理赞成小李聚餐费用800.0元的请求 ret3=总经理赞成小李聚餐费用1600.0元的请求
(6)接下来看看如何在不改动现有的框架的前提下,扩展新的业务,这样才能说明这种设计的灵活性。
假如就是要实现上面示例过的另一个功能“预支差旅费申请”吧。要想扩展新的业务,第一步就是新建一个封装业务数据的对象,示例代码以下:
/** * 封装跟预支差旅费申请业务相关的请求数据 */ public class PreFeeRequestModel extends RequestModel{ /** * 约定具体的业务类型 */ public final static String FEE_TYPE = "preFee"; public PreFeeRequestModel() { super(FEE_TYPE); } /** * 申请人 */ private String user; /** * 申请金额 */ private double fee; public String getUser() { return user; } public void setUser(String user) { this.user = user; } public double getFee() { return fee; } public void setFee(double fee) { this.fee = fee; } }
有些朋友会发现,这个对象跟封装聚餐费用申请业务数据的对象几乎彻底同样的,这里要说明一下,同样的缘由主要是咱们为了演示简单,设计得类似,实际业务中多是不同的,所以,最好仍是一个业务一个对象,若是确实有公共的数据,能够定义公共的父类,最好不要让不一样的业务使用统一个对象,容易混淆。
(7)对于具体进行职责处理的类,比较好的方式就是扩展出子类来,而后在子类里面实现新加入的业务,固然也能够直接在原来的对象上改
。仍是采用扩展出子类的方式吧,仍是看看新的项目经理的处理类,示例代码以下:
/** * 实现为项目经理增长预支差旅费用申请处理的功能的子对象, * 如今的项目经理既能够处理聚餐费用申请,又能够处理预支差旅费用申请 */ public class ProjectManager2 extends ProjectManager{ public Object handleRequest(RequestModel rm){ if(PreFeeRequestModel.FEE_TYPE.equals(rm.getType())){ //表示预支差旅费用申请 return myHandler(rm); }else{ //其它的让父类去处理 return super.handleRequest(rm); } } private Object myHandler(RequestModel rm) { //先把通用的对象造型回来 PreFeeRequestModel frm = (PreFeeRequestModel)rm; //项目经理的权限比较小,只能在5000之内 if(frm.getFee() < 5000){ //工做须要嘛,通通赞成 System.out.println("项目经理赞成"+frm.getUser()+"预支差旅费用"+frm.getFee()+"元的请求"); return true; }else{ //超过5000,继续传递给级别更高的人处理 if(this.successor!=null){ return this.successor.handleRequest(rm); } } return false; } }
部门经理和总经理的处理相似,就不去示例了。
(8)看看此时的测试,示例以下:
public class Client { public static void main(String[] args) { //先要组装职责链 Handler h1 = new GeneralManager2(); Handler h2 = new DepManager2(); Handler h3 = new ProjectManager2(); h3.setSuccessor(h2); h2.setSuccessor(h1); //开始测试申请聚餐费用 FeeRequestModel frm = new FeeRequestModel(); frm.setFee(300); frm.setUser("小李"); //调用处理 String ret1 = (String)h3.handleRequest(frm); System.out.println("ret1="+ret1); //从新设置申请金额,再调用处理 frm.setFee(800); h3.handleRequest(frm); String ret2 = (String)h3.handleRequest(frm); System.out.println("ret2="+ret2); //从新设置申请金额,再调用处理 frm.setFee(1600); h3.handleRequest(frm); String ret3 = (String)h3.handleRequest(frm); System.out.println("ret3="+ret3); //开始测试申请预支差旅费用 PreFeeRequestModel pfrm = new PreFeeRequestModel(); pfrm.setFee(3000); pfrm.setUser("小张"); //调用处理 h3.handleRequest(pfrm); //从新设置申请金额,再调用处理 pfrm.setFee(6000); h3.handleRequest(pfrm); //从新设置申请金额,再调用处理 pfrm.setFee(36000); h3.handleRequest(pfrm); } }
运行一下,试试看,运行结果以下:
ret1=项目经理赞成小李聚餐费用300.0元的请求 ret2=部门经理赞成小李聚餐费用800.0元的请求 ret3=总经理赞成小李聚餐费用1600.0元的请求 项目经理赞成小张预支差旅费用3000.0元的请求 部门经理赞成小张预支差旅费用6000.0元的请求 总经理赞成小张预支差旅费用36000.0元的请求
好好体会一下这种设计方式的好处,至关的通用和灵活,有了新业务,只须要添加实现新功能的对象就能够了,可是带来的缺陷就是可能会形成对象层次过多,或者出现较多的细粒度的对象,极端状况下,每次就扩展一个方法,会出现大量只处理一个功能的细粒度对象
。
##3.3 功能链## 在实际开发中,常常会出现一个把职责链稍稍变形的用法。在标准的职责链中,一个请求在职责链中传递,只要有一个对象处理了这个请求,就会中止
。
如今稍稍变一下,改为一个请求在职责链中传递,每一个职责对象负责处理请求的某一方面的功能,处理完成后,不是中止,而是继续向下传递请求,当请求经过不少职责对象处理事后,功能也就处理完了,把这样的职责链称为功能链
。
考虑这样一个功能,在实际应用开发中,在进行业务处理以前,一般须要进行权限检查、通用数据校验、数据逻辑校验等处理,而后才开始真正的业务逻辑实现。能够把这些功能分散到一个功能链中,这样作的目的是使程序结构更加灵活,并且复用性会更好,好比通用的权限检查就只须要作一份,而后就能够在多个功能链中使用了。
有些朋友看到这里,可能会想,这不是可使用装饰模式来实现吗?没错,可使用装饰模式来实现这样的功能,可是职责链会更灵活一些,由于装饰模式是在已有的功能上增长新的功能,多个装饰器之间会有必定的联系
;而职责链模式的各个职责对象实现的功能,相互之间是没有关联的,是本身实现属于本身处理的那一份功能
。
可能有些朋友会想到这很相似于在Web应用开发中的过滤器Filter,没错,过滤器链就相似于一个功能链,每一个过滤器负责本身的处理,而后转交给下一个过滤器,直到把全部的过滤器都走完,而后进入到Servlet里面进行处理
。最多见的过滤器功能,好比权限检查、字符集转换等,基本上都是Web应用的标配。
接下来在示例中,实现这样的功能:实现商品销售的业务处理,在真正进行销售的业务处理以前,须要对传入处理的数据,进行权限检查、通用数据检查和数据逻辑检查,只有这些检查都能经过的状况下,才说明传入的数据是正确的、有效的数据,才能够进行真正的业务功能处理。
/** * 商品销售管理模块的业务处理 */ public class GoodsSaleEbo { /** * 保存销售信息,原本销售数据应该是多条,太麻烦了,为了演示,简单点 * @param user 操做人员 * @param customer 客户 * @param saleModel 销售数据 * @return 是否保存成功 */ public boolean sale(String user,String customer,SaleModel saleModel){ //若是所有在这里处理,基本的顺序是 //1:权限检查 //2:通用数据检查(这个也可能在表现层已经做过了) //3:数据逻辑校验 //4:真正的业务处理 //可是如今经过功能链来作,这里就主要负责构建链 //暂时尚未功能链,等实现好了各个处理对象再回来添加 return true; } }
对应的封装销售数据的对象,示例代码以下:
/** * 封装销售单的数据,简单的示意一下 */ public class SaleModel { /** * 销售的商品 */ private String goods; /** * 销售的数量 */ private int saleNum; public String getGoods() { return goods; } public void setGoods(String goods) { this.goods = goods; } public int getSaleNum() { return saleNum; } public void setSaleNum(int saleNum) { this.saleNum = saleNum; } public String toString(){ return "商品名称="+goods+",销售数量="+saleNum; } }
/** * 定义职责对象的接口 */ public abstract class SaleHandler { /** * 持有下一个处理请求的对象 */ protected SaleHandler successor = null; /** * 设置下一个处理请求的对象 * @param successor 下一个处理请求的对象 */ public void setSuccessor(SaleHandler successor){ this.successor = successor; } /** * 处理保存销售信息的请求 * @param user 操做人员 * @param customer 客户 * @param saleModel 销售数据 * @return 是否处理成功 */ public abstract boolean sale(String user,String customer,SaleModel saleModel); }
/** * 进行权限检查的职责对象 */ public class SaleSecurityCheck extends SaleHandler{ public boolean sale(String user, String customer, SaleModel saleModel) { //进行权限检查,简单点,就小李能经过 if("小李".equals(user)){ return this.successor.sale(user, customer, saleModel); }else{ System.out.println("对不起"+user+",你没有保存销售信息的权限"); return false; } } }
接下来定义通用数据检查的职责对象,示例代码以下:
/** * 进行数据通用检查的职责对象 */ public class SaleDataCheck extends SaleHandler{ public boolean sale(String user, String customer, SaleModel saleModel) { //进行数据通用检查,稍麻烦点,每一个数据都要检测 if(user==null || user.trim().length()==0){ System.out.println("申请人不能为空"); return false; } if(customer==null || customer.trim().length()==0){ System.out.println("客户不能为空"); return false; } if(saleModel==null ){ System.out.println("销售商品的数据不能为空"); return false; } if(saleModel.getGoods() == null || saleModel.getGoods().trim().length()==0){ System.out.println("销售的商品不能为空"); return false; } if(saleModel.getSaleNum()==0){ System.out.println("销售商品的数量不能为0"); return false; } //若是经过了上面的检测,那就向下继续执行 return this.successor.sale(user, customer, saleModel); } }
再看看进行数据逻辑检查的职责对象,示例代码以下:
/** * 进行数据逻辑检查的职责对象 */ public class SaleLogicCheck extends SaleHandler{ public boolean sale(String user, String customer, SaleModel saleModel) { //进行数据的逻辑检查,好比检查ID的惟一性,主外键的对应关系等等 //这里应该检查这种主外键的对应关系,好比销售商品是否存在 //为了演示简单,直接经过吧 //若是经过了上面的检测,那就向下继续执行 return this.successor.sale(user, customer, saleModel); } }
最后是真正的业务处理的职责对象,示例代码以下:
/** * 真正处理销售的业务功能的职责对象 */ public class SaleMgr extends SaleHandler{ public boolean sale(String user, String customer, SaleModel saleModel) { //进行真正的业务逻辑处理 System.out.println(user+"保存了"+customer+"购买 "+saleModel+" 的销售数据"); return true; } }
public class GoodsSaleEbo { /** * 保存销售信息,原本销售数据应该是多条,太麻烦了,为了演示,简单点 * @param user 操做人员 * @param customer 客户 * @param saleModel 销售数据 * @return 是否保存成功 */ public boolean sale(String user,String customer,SaleModel saleModel){ //若是所有在这里处理,基本的顺序是 //1:权限检查 //2:通用数据检查(这个也可能在表现层已经做过了) //3:数据逻辑校验 //4:真正的业务处理 //可是如今经过功能链来作,这里就主要负责构建链 SaleSecurityCheck ssc = new SaleSecurityCheck(); SaleDataCheck sdc = new SaleDataCheck(); SaleLogicCheck slc = new SaleLogicCheck(); SaleMgr sd = new SaleMgr(); ssc.setSuccessor(sdc); sdc.setSuccessor(slc); slc.setSuccessor(sd); //向链上的第一个对象发出处理的请求 return ssc.sale(user, customer, saleModel); } }
public class Client { public static void main(String[] args) { //建立业务对象 GoodsSaleEbo ebo = new GoodsSaleEbo(); //准备测试数据 SaleModel saleModel = new SaleModel(); saleModel.setGoods("张学友怀旧经典"); saleModel.setSaleNum(10); //调用业务功能 ebo.sale("小李", "张三", saleModel); ebo.sale("小张", "李四", saleModel); } }
运行一下,试试看,运行结果以下:
小李保存了张三购买 商品名称=张学友怀旧经典,销售数量=10 的销售数据 对不起小张,你没有保存销售信息的权限
##3.4 职责链模式的优缺点##
在职责链模式里面,请求者并不知道接收者是谁,也不知道具体如何处理,请求者只是负责向职责链发出请求就能够了
。而每一个职责对象也不用管请求者或者是其它的职责对象,只负责处理本身的部分,其它的就交由其它的职责对象去处理
。也就是说,请求者和接收者是彻底解耦的。
职责链模式会把功能处理分散到单独的职责对象里面,而后在使用的时候,能够动态组合职责造成职责链,从而能够灵活的给对象分配职责,也能够灵活的实现和改变对象的职责。
职责链模式会把功能处理分散到单独的职责对象里面,也就是每一个职责对象只是处理一个方面的功能,要把整个业务处理完,须要大量的职责对象的组合,这会产生大量的细粒度职责对象。
职责链模式的每一个职责对象只负责本身处理的那一部分,所以可能会出现某个请求,把整个链传递完了,都没有职责对象处理它。这就须要在使用职责链模式的时候注意,须要提供默认的处理,而且注意构建的链的有效性
。
##3.5 思考职责链模式##
职责链模式的本质:分离职责,动态组合。
分离职责是前提,只有先把复杂功能分开,拆分红不少的步骤和小的功能处理,而后才能合理规划和定义职责类
,能够有不少的职责类来负责处理某一个功能,让每一个职责类负责处理功能的某一个方面,在运行期间进行动态组合,造成一个处理的链,把这个链运行完,那么功能也就处理完了。
动态组合才是职责链模式的精华所在,由于要实现请求对象和处理对象的解耦,请求对象不知道谁才是真正的处理对象
,所以要动态的把可能的处理对象组合起来,因为组合的方式是动态的,这就意味着能够很方便的修改和添加新的处理对象,从而让系统更加灵活和具备更好的扩展性。
固然这么作还会有一个潜在的优势,就是能够加强职责功能的复用性。若是职责功能是不少地方均可以使用的公共功能,那么它能够应用在多个职责链中复用。
建议在以下状况中,选用职责链模式:
若是有多个对象能够处理同一个请求,可是具体由哪一个对象来处理该请求,是运行时刻动态肯定的
。这种状况可使用职责链模式,把处理请求的对象实现成为职责对象,而后把它们构成一个职责链,当请求在这个链中传递的时候,具体由哪一个职责对象来处理,会在运行时动态判断。
若是你想在不明确指定接收者的状况下,向多个对象中的一个提交一个请求的话,可使用职责链模式
,职责链模式实现了请求者和接收者之间的解耦,请求者不须要知道到底是哪个接收者对象来处理了请求。
若是想要动态指定处理一个请求的对象集合,可使用职责链模式,职责链模式能动态的构建职责链
,也就是动态的来决定到底哪些职责对象来参与处处理请求中来,至关因而动态指定了处理一个请求的职责对象集合。
##3.6 相关模式##
这两个模式能够组合使用。
能够把职责对象经过组合模式来组合
,这样能够经过组合对象自动递归的向上调用,由父组件做为子组件的后继,从而造成链。
这也就是前面提到过的使用外部已有的连接,这种状况在客户端使用的时候,就不用再构造链了,虽然不构造链,可是须要构造组合对象树,是同样的。
这两个模式类似,从某个角度讲,能够相互模拟实现对方的功能
。
装饰模式可以动态的给被装饰对象添加功能,要求装饰器对象和被装饰的对象实现相同的接口
。而职责链模式能够实现动态的职责组合,标准的功能是有一个对象处理就结束,可是若是处理完本职责不急于结束,而是继续向下传递请求
,那么功能就和装饰模式的功能差很少了,每一个职责对象就相似于装饰器,能够实现某种功能。
并且两个模式的本质也相似,都须要在运行期间动态组合,装饰模式是动态组合装饰器,而职责链是动态组合处理请求的职责对象的链
。
可是从标准的设计模式上来说,这两个模式仍是有较大区别的,这点要注意。首先是目的不一样,装饰模式是要实现透明的为对象添加功能
,而职责链模式是要实现发送者和接收者解耦
;另一个,装饰模式是无限递归调用的,能够有任意多个对象来装饰功能,可是职责链模式是有一个处理就结束
。
这两个模式能够组合使用。
这两个模式有类似之处,若是把职责链简化到直接就能选择到相应的处理对象,那就跟策略模式的选择差很少
,所以能够用职责链来模拟策略模式的功能。只是若是把职责链简化到这个地步,也就不存在链了,也就称不上是职责链了。
两个模式能够组合使用,能够在职责链模式的某个职责的实现的时候,使用策略模式来选择具体的实现,一样也能够在策略模式的某个策略实现里面,使用职责链模式来实现功能处理
。
同理职责链模式也能够和状态模式组合使用。