23.3 模式讲解
23.3.1 认识职责链模式
(1)模式功能
职责链模式主要用来处理:“客户端发出一个请求,有多个对象都有机会来处理这一个请求,但是客户端不知道究竟谁会来处理他的请求”,这样的情况。也就是需要让请求者和接收者解耦,这样就可以动态的切换和组合接收者了。
要注意在标准的职责链模式里面,是只要有对象处理了请求,这个请求就到此为止,不再被传递和处理了。
如果是要变形使用职责链,就可以让这个请求继续传递,每个职责对象对这个请求进行一定的功能处理,从而形成一个处理请求的功能链。
(2)隐式接收者
当客户端发出请求的时候,客户端并不知道谁会真正处理他的请求,客户端只知道他提交请求的第一个对象。从第一个处理对象开始,整个职责链里面的对象,要么自己处理请求,要么继续转发给下一个接收者。
也就是对于请求者而言,并不知道最终的接收者是谁,但是一般情况下,总是会有一个对象来处理的,因此称为隐式接收者。
(3)如何构建链
职责链的链怎么构建呢?这是个大问题,实现的方式也是五花八门,归结起来大致有以下一些方式。
首先是按照实现的地方来说:
可以实现在客户端,在提交请求前组合链,也就是在使用的时候动态组合链,称为外部链;
也可以在Handler里面实现链的组合,算是内部链的一种;
当然还有一种就是在各个职责对象里面,由各个职责对象自行决定后续的处理对象,这种实现方式要求每个职责对象除了进行业务处理外,还必须了解整个业务流程。
按照构建链的数据来源,也就是决定了按照什么顺序来组合链的数据,又分为几种:
一种就是在程序里面动态组合;
也可以通过外部,比如数据库来获取组合的数据,这种属于数据库驱动的方式;
还有一种方式就是通过配置文件传递进来,也可以是流程的配置文件。
如果是从外部获取数据来构建链,那么在程序运行的时候,会读取这些数据,然后根据数据的要求来获取相应的对象,并组合起来。
还有一种是不需要构建链,因为已有的对象已经自然构成链了,这种情况多出现在组合模式构建的对象树中,这样子对象可以很自然的向上找到自己的父对象。就像部门人员的
组织结构一样,顶层是总经理,总经理下面是各个部门的经理,部门经理下面是
项目经理,项目经理下面是各个普通员工,自然就可以形成:普通员工à项目经理à部门经理à总经理这样的链。
(4)谁来处理
职责链中那么多处理对象,到底谁来处理请求呢,这个是在运行时期动态决定的。当请求被传递到某个处理对象的时候,这个对象会按照已经设定好的条件来判断,是否属于自己处理的范围,如果是就处理,如果不是就转发请求给下一个对象。
(5)请求一定会被处理吗?
在职责链模式中,请求不一定会被处理,因为可能没有合适的处理者,请求在职责链里面从头传递到尾,每个处理对象都判断不属于自己处理,最后请求就没有对象来处理。这一点是需要注意的。
可以在职责链的末端始终加上一个不支持此功能处理的职责对象,这样如果传递到这里,就会出现提示,本职责链没有对象处理这个请求。
23.3.2 处理多种请求
前面的示例都是同一个职责链处理一种请求的情况,现在有这样的需求,还是费用申请的功能,这次是申请预支差旅费,假设还是同一流程,也就是组合同一个职责链,从项目经理à传递给部门经理à传递给总经理,虽然流程相同,但是每个处理类需要处理两种请求,它们的具体业务逻辑是不一样的,那么该如何实现呢?
1:简单的处理方式
要解决这个问题,也不是很困难,一个简单的方法就是为每种业务单独定义一个方法,然后客户端根据不同的需要调用不同的方法,还是通过代码来示例一下。注意这里故意的把两个方法做的有些不一样,一个是返回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元的请求
2:通用请求的处理方式
上面的实现看起来很容易,但是仔细想想,这样实现有没有什么问题呢?
这种实现方式有一个很明显的问题,那就是只要增加一个业务,就需要修改职责的接口,这是很不灵活的,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元的请求
好好体会一下这种设计方式的好处,相当的通用和灵活,有了新业务,只需要添加实现新功能的对象就可以了,但是带来的缺陷就是可能会造成对象层次过多,或者出现较多的细粒度的对象,极端情况下,每次就扩展一个方法,会出现大量只处理一个功能的细粒度对象。
23.3.3 功能链
在实际开发中,经常会出现一个把职责链稍稍变形的用法。在标准的职责链中,一个请求在职责链中传递,只要有一个对象处理了这个请求,就会停止。
现在稍稍变一下,改成一个请求在职责链中传递,每个职责对象负责处理请求的某一方面的功能,处理完成后,不是停止,而是继续向下传递请求,当请求通过很多职责对象处理过后,功能也就处理完了,把这样的职责链称为功能链。
考虑这样一个功能,在
实际应用开发中,在进行业务处理之前,通常需要进行权限检查、通用数据校验、数据逻辑校验等处理,然后才开始真正的业务逻辑实现。可以把这些功能分散到一个功能链中,这样做的目的是使程序结构更加灵活,而且复用性会更好,比如通用的权限检查就只需要做一份,然后就可以在多个功能链中使用了。
有些朋友看到这里,可能会想,这不是可以使用
装饰模式来实现吗?没错,可以使用装饰模式来实现这样的功能,但是职责链会更灵活一些,因为装饰模式是在已有的功能上增加新的功能,多个装饰器之间会有一定的联系;而职责链模式的各个职责对象实现的功能,相互之间是没有关联的,是自己实现属于自己处理的那一份功能。
可能有些朋友会想到这很类似于在Web应用开发中的过滤器Filter,没错,过滤器链就类似于一个功能链,每个过滤器负责自己的处理,然后转交给下一个过滤器,直到把所有的过滤器都走完,然后进入到
Servlet里面进行处理。最常见的过滤器功能,比如权限检查、字符集转换等,基本上都是Web应用的标配。
接下来在示例中,实现这样的功能:实现商品销售的业务处理,在真正进行销售的业务处理之前,需要对传入处理的数据,进行权限检查、通用数据检查和数据逻辑检查,只有这些检查都能通过的情况下,才说明传入的数据是正确的、有效的数据,才可以进行真正的业务功能处理。
(1)首先定义已有的业务功能和封装业务数据的对象,用前面出现过的那个保存销售信息的业务,为了简单,就不去定义接口了,示例代码如下:
/**
/**
* 商品销售管理模块的业务处理
*/
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;
}
}
(2)定义一个用来处理保存销售数据功能的职责对象的接口,示例代码如下:
/**
* 定义职责对象的接口
*/
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);
}
(3)实现各个职责处理对象,每个职责对象负责请求的一个方面的处理,把这些职责对象都走完了,功能也就实现完了。先定义处理安全检查的职责对象,示例代码如下:
/**
* 进行权限检查的职责对象
*/
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;
}
}
(4)实现好了各个职责对象处理,回过头来看看如何具体实现业务处理,在业务对象里面进行功能链的组合,示例代码如下:
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);
}
}
(5)写个客户端,调用业务对象,测试一下看看,示例代码如下:
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 的销售数据
对不起小张,你没有保存销售信息的权限
你还可以改变测试的数据,看看效果,好好体会这种设计的灵活性,很多框架级功能的设计都用得上。
23.3.4 职责链模式的优缺点
l 请求者和接收者松散耦合
在职责链模式里面,请求者并不知道接收者是谁,也不知道具体如
何处理,请求者只是负责向职责链发出请求就可以了。而每个职责对象也不用管请求者或者是其它的职责对象,只负责处理自己的部分,其它的就交由其它的职责对象去处理。也就是说,请求者和接收者是完全解耦的。
l 动态组合职责
职责链模式会把功能处理分散到单独的职责对象里面,然后在使用的时候,可以动态组合职责形成职责链,从而可以灵活的给对象分配职责,也可以灵活的实现和改变对象的职责。
l 产生很多细粒度对象
职责链模式会把功能处理分散到单独的职责对象里面,也就是每个职责对象只是处理一个方面的功能,要把整个业务处理完,需要大量的职责对象的组合,这会产生大量的细粒度职责对象。
l 不一定能被处理
职责链模式的每个职责对象只负责自己处理的那一部分,因此可能会出现某个请求,把整个链传递完了,都没有职责对象处理它。这就需要在使用职责链模式的时候注意,需要提供默认的处理,并且注意构建的链的有效性。
23.3.5 思考职责链模式
1:职责链模式的本质
职责链模式的本质:分离职责,动态组合。
分离职责是前提,只有先把复杂功能分开,拆分成很多的步骤和小的功能处理,然后才能
合理规划和定义职责类,可以有很多的职责类来负责处理某一个功能,让每个职责类负责处理功能的某一个方面,在运行期间进行动态组合,形成一个处理的链,把这个链运行完,那么功能也就处理完了。
动态组合才是职责链模式的精华所在,因为要实现请求对象和处理对象的解耦,请求对象不知道谁才是真正的处理对象,因此要动态的把可能的处理对象组合起来,由于组合的方式是动态的,这就意味着可以很方便的修改和添加新的处理对象,从而让系统更加灵活和具有更好的
扩展性。
当然这么做还会有一个潜在的优点,就是可以增强职责功能的复用性。如果职责功能是很多地方都可以使用的公共功能,那么它可以应用在多个职责链中复用。
2:何时选用职责链模式
建议在如下情况中,选用职责链模式:
如果有多个对象可以处理同一个请求,但是具体由哪个对象来处理该请求,是运行时刻动态确定的。这种情况可以使用职责链模式,把处理请求的对象实现成为职责对象,然后把它们构成一个职责链,当请求在这个链中传递的时候,具体由哪个职责对象来处理,会在运行时动态判断。
如果你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求的话,可以使用职责链模式,职责链模式实现了请求者和接收者之间的解耦,请求者不需要知道究竟是哪一个接收者对象来处理了请求。
如果想要动态指定处理一个请求的对象集合,可以使用职责链模式,职责链模式能动态的构建职责链,也就是动态的来决定到底哪些职责对象来参与到处理请求中来,相当于是动态指定了处理一个请求的职责对象集合。
23.3.6 相关模式
l 职责链模式和组合模式
这两个模式可以组合使用。
可以把职责对象通过组合模式来组合,这样可以通过组合对象自动
递归的向上调用,由父组件作为子组件的后继,从而形成链。
这也就是前面提到过的使用外部已有的链接,这种情况在客户端使用的时候,就不用再构造链了,虽然不构造链,但是需要构造组合对象树,是一样的。
l 职责链模式和装饰模式
这两个模式相似,从某个角度讲,可以相互模拟实现对方的功能。
装饰模式能够动态的给被装饰对象添加功能,要求装饰器对象和被装饰的对象实现相同的接口。而职责链模式可以实现动态的职责组合,标准的功能是有一个对象处理就结束,但是如果处理完本职责不急于结束,而是继续向下传递请求,那么功能就和装饰模式的功能差不多了,每个职责对象就类似于装饰器,可以实现某种功能。
而且两个模式的本质也类似,都需要在运行期间动态组合,装饰模式是动态组合装饰器,而职责链是动态组合处理请求的职责对象的链。
但是从标准的
设计模式上来讲,这两个模式还是有较大区别的,这点要注意。首先是目的不同,装饰模式是要实现透明的为对象添加功能,而职责链模式是要实现发送者和接收者解耦;另外一个,装饰模式是无限
递归调用的,可以有任意多个对象来装饰功能,但是职责链模式是有一个处理就结束。
l 职责链模式和策略模式
这两个模式可以组合使用。
这两个模式有相似之处,如果把职责链简化到直接就能选择到相应的处理对象,那就跟策略模式的选择差不多,因此可以用职责链来模拟策略模式的功能。只是如果把职责链简化到这个地步,也就不存在链了,也就称不上是职责链了。
两个模式可以组合使用,可以在职责链模式的某个职责的实现的时候,
使用策略模式来选择具体的实现,同样也可以在策略模式的某个策略实现里面,使用职责链模式来实现功能处理。
同理职责链模式也可以和状态模式组合使用。