收藏私塾在线
 

欢迎您来到私塾在线网!   

请登录! 

免费注册 


javasscc的笔记
状态: 离线
人气:515735
访问用户量:1155
笔记经验:
总积分:1958
级别:普通会员
搜索本笔记
ta的交流分类
ta的交流主题贴(196)
ta的所有交流贴(200)
ta的全部笔记
全部笔记(98)
未分类笔记(0)
研磨设计模式(94)
课程问题(0)
设计模式综合项目实战(4)
存档
2013-05(4)
2012-08(39)
2012-07(55)

2012-08-24 09:56:03
研磨设计模式 之 职责链模式(Chain of Responsibility)3——跟着cc学设计系列
浏览(5452)|评论(0)   交流分类:Java|笔记分类: 研磨设计模式

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


---------------------------------------------------------------------------

私塾在线学习网原创内容  跟着cc学设计系列 之 研磨设计模式

研磨设计讨论群【252780326】

原创内容,转载请注明出处【http://sishuok.com/forum/blogPost/list/0/5815.html

---------------------------------------------------------------------------

相关笔记推荐
精品视频课程推荐

Java数据结构和算法精讲版
本课程专注于数据结构和算法的内容,使用Java来进行代码示例,不空洞的讲解概念和理论,重点放在代码的实现和示例上。 从零开始、全面系统、成体系的讲解数据结构和基本算法,循序渐进的讲述构建软件系统所常见的数据结构和算法。

软件系统功能设计实战训练视频教程
本课程是《软件系统功能设计实战训练》网络班的全套学习视频,通过6个具体的设计案例,综合应用几乎全部的GoF设计模式,以及多种设计理念和方法。 每个设计案例都各有侧重点,训练的难度会逐步加大,以切实帮助各位学员快速的掌握软件系统设计的方法,提升自己的软件系统设计能力。

Ajax+JSON基础实战视频教程
数据校验、Javascript模拟多线程、下拉列表联动、操作XML、AJAX结合JSON的操作、Json-lib的使用

Weblogic实战视频教程
WebLogic基础知识:WebLogic基本概念、正确安装WebLogic、建域、应用部署于JDBC选择、对WebLogic的监控和日志查看、集群的高可用性;课程目标:彻底掌握WebLogic的基本概念,在理解基本概念的基础上做到正确的安装WebLogic,根据不同的需求创建域,合理选择应用部署和JDBC配置。熟练掌握WebLogic的console监控,了解各种性能和运行指标,以及对监控结果的分析,运用集群的高可用性,对集群架设。

freevoice(局域网对讲机)——Android4项目实战视频教程
实现通过Wifi来在局域网内相互传递信息和语音通话的功能;学习Service;Broadcast Receiver;SharedPreference;SQLite;语音录制、播放、传输等;网络开发:wifi处理、UDP编程、TCP编程;多种设计模式的应用 等等

浏览(5452)|评论(0)   交流分类:Java|笔记分类: 研磨设计模式

评论(0)
请登录后评论 登录

关于我们 | 联系我们 | 用户协议 | 私塾在线服务协议 | 版权声明 | 隐私保护

版权所有 Copyright(C)2009-2012 私塾在线学习网