`
Atermojn
  • 浏览: 2998 次
文章分类
社区版块
存档分类
最新评论

研磨设计模式 之 职责链模式(Chain of Responsibility)3——跟着cc学设计

阅读更多
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          职责链模式和策略模式
    这两个模式可以组合使用。
    这两个模式有相似之处,如果把职责链简化到直接就能选择到相应的处理对象,那就跟策略模式的选择差不多,因此可以用职责链来模拟策略模式的功能。只是如果把职责链简化到这个地步,也就不存在链了,也就称不上是职责链了。
    两个模式可以组合使用,可以在职责链模式的某个职责的实现的时候,使用策略模式来选择具体的实现,同样也可以在策略模式的某个策略实现里面,使用职责链模式来实现功能处理。
    同理职责链模式也可以和状态模式组合使用。
分享到:
评论

相关推荐

    研磨设计模式(完整带书签).part2.pdf

    本电子书一共两个压缩文档,本文件为part2. ...第23章 职责链模式(Chain of Responsibility) 第24章 桥接模式(Bridge) 第25章 访问者模式(Visitor) 附录A常见面向对象设计原则 附录BUML简介 参考文献

    研磨设计模式(完整带书签).part1.pdf

    本电子书一共两个压缩文档,该文档为part1。...第23章 职责链模式(Chain of Responsibility) 第24章 桥接模式(Bridge) 第25章 访问者模式(Visitor) 附录A常见面向对象设计原则 附录BUML简介 参考文献

    研磨设计模式带书签完整版228M.7z.001

    《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...

    研磨设计模式源码

    研磨设计模式的配套源码,请下载,不要想了,解压后,直接放到eclipse下面即可运行!

    研磨设计模式-part3

    完整清晰版,完美书签. 《研磨设计模式》完整覆盖GoF...第23章 职责链模式(Chain of Responsibility) 第24章 桥接模式(Bridge) 第25章 访问者模式(Visitor) 附录A常见面向对象设计原则 附录BUML简介 参考文献

    研磨设计模式 完美书签 完整(一)

    《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...

    研磨设计模式-part2

    完整清晰版,完美书签. 《研磨设计模式》完整覆盖GoF...第23章 职责链模式(Chain of Responsibility) 第24章 桥接模式(Bridge) 第25章 访问者模式(Visitor) 附录A常见面向对象设计原则 附录BUML简介 参考文献

    研磨设计模式.part3(共4个)

    完整清晰版,完美书签. 《研磨设计模式》完整...第23章 职责链模式(Chain of Responsibility) 第24章 桥接模式(Bridge) 第25章 访问者模式(Visitor) 附录A常见面向对象设计原则 附录BUML简介 参考文献

    研磨设计模式-part4

    完整清晰版,完美书签. 《研磨设计模式》完整覆盖GoF...第23章 职责链模式(Chain of Responsibility) 第24章 桥接模式(Bridge) 第25章 访问者模式(Visitor) 附录A常见面向对象设计原则 附录BUML简介 参考文献

    研磨设计模式带书签完整版228M.7z.002

    《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...

    研磨设计模式(完整版pdf) part1 (1-3)

    研磨设计模式(完整版pdf)相信我不用介绍,很多人都在找这本书吧,今天我特意从另外的地方下载到了这本书,发了几十积分。才弄到。为了让大家同时分享这本书的精华。我今天特意上传。注:此电子书较大,我分了3个...

    研磨设计模式-陈臣.epub

    “1.1 设计模式是什么 1.1.1 什么是模式 从字面上理解,模,就是模型、模板的意思;式,就是方式、方法的意思。综合起来,所谓模式就是:可以作为模型或模板的方式或方法。... “研磨设计模式”。 iBooks.

    研磨设计模式(完整版pdf)part3 (3-3)

    研磨设计模式(完整版pdf)相信我不用介绍,很多人都在找这本书吧,今天我特意从另外的地方下载到了这本书,发了几十积分。才弄到。为了让大家同时分享这本书的精华。我今天特意上传。注:此电子书较大,我分了3个...

    研磨设计模式.part2(共4个)

    完整清晰版,完美书签. 《研磨设计模式》完整...第23章 职责链模式(Chain of Responsibility) 第24章 桥接模式(Bridge) 第25章 访问者模式(Visitor) 附录A常见面向对象设计原则 附录BUML简介 参考文献

    研磨设计模式.part4(共4个)

    完整清晰版,完美书签. 《研磨设计模式》完整...第23章 职责链模式(Chain of Responsibility) 第24章 桥接模式(Bridge) 第25章 访问者模式(Visitor) 附录A常见面向对象设计原则 附录BUML简介 参考文献

    研磨设计模式.part1(共4个)

    完整清晰版,完美书签. 《研磨设计模式》完整...第23章 职责链模式(Chain of Responsibility) 第24章 桥接模式(Bridge) 第25章 访问者模式(Visitor) 附录A常见面向对象设计原则 附录BUML简介 参考文献

    研磨设计模式视频课程PPT

    内容概述:本视频课程是北京Java私塾原创精品书籍《研磨设计模式》一书的配套学习视频,由《研磨设计模式》的第一作者CC录制 课程目标:全面、系统的掌握GoF设计模式的知识,达到可以在实际项目开发中运用的能力 ...

    研磨设计模式PDF

    研磨设计模式PDF

    研磨设计模式-陈臣.王斌.扫描高清版PDF

    设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。 使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;...

    研磨设计模式(完整版pdf)part2 (2-3)

    研磨设计模式(完整版pdf)相信我不用介绍,很多人都在找这本书吧,今天我特意从另外的地方下载到了这本书,发了几十积分。才弄到。为了让大家同时分享这本书的精华。我今天特意上传。注:此电子书较大,我分了3个...

Global site tag (gtag.js) - Google Analytics