现在的位置: 首页 > 综合 > 正文

责任链模式【Chain of Responsibility Pattern】

2018年05月12日 ⁄ 综合 ⁄ 共 10010字 ⁄ 字号 评论关闭

中国古代对妇女制定了“三从四德”的道德规范,“三从”是指“未嫁从父、既嫁从夫、夫死从子”,也就是说一个女性,在没有结婚的时候要听从于父亲,结了婚后听从于丈夫,丈夫死了还要听儿子的,举个例子来说,一个女的要出去逛街,同样这样的一个请求,在她没有出嫁前她必须征得父亲的同意,出嫁之后必须获得丈夫的许可,那丈夫死了怎么办?一般都是男的比女的死的早,还要问问儿子是否允许自己出去逛街,估计你下边马上要问要是没有儿子怎么办?请示小叔子、侄子等等,在父系社会中,妇女只占从属地位,现在想想中国的妇女还是比较悲惨的,逛个街还要请示来请示去,而且作为父亲、丈夫、儿子只有两种选择:要不承担起责任来告诉她允许或不允许逛街,要不就让她请示下一个人,这是整个社会体系的约束,应用到我们项目中就是业务规则,那我们来看怎么把“三从”通过我们的程序来实现,需求很简单:通过程序描述一下古代妇女的“三从”制度,好我们先看类图:

194

您的设计模式

非常简单的类图,这个实现也非常简单,我们先看 IWomen 接口:

package
com.cbf4life.common;

/**
*
@author
cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.*
古代悲哀女性的总称
*/

public interface
IWomen {//获得个人状况

public int
getType();

//获得个人请示,你要干什么?出去逛街?约会?还是看电影

public
String getRequest();}

女性就两个参数,一个是当前的个人状况,是结婚了呢还是没结婚,丈夫有没有去世,另外一个是要请示的内容,要出去逛街呀还是吃饭,我们看实现类:

package
com.cbf4life.common;/**

*
@author
cbf4Life cbf4life@126.com
195

您的设计模式

    * I'm glad to share my knowledge with you all.

*
古代女性的总称

*/

public class
Women
implements
IWomen{/*

  • *  通过一个int类型的参数来描述妇女的个人状况

  • *  1---未出嫁

  • *  2---出嫁

  • *  3---夫死

    */

    private int
    type=0;//妇女的请示

    private
    String
    request
    =
    "";

    //构造函数传递过来请求
    public
    Women(int
    _type,String _request){

    this.type
    = _type;

    this.request
    = _request;}

    //获得自己的状况
    public int
    getType(){

    return this.type;}

    //获得妇女的请求
    public
    String getRequest(){

    return this.request;}

    }

    我们使用数字来代表女性的不同状态,1 是未结婚,2 是已经结婚的,而且丈夫建在,3 是丈夫去世了的。我们再来看有处理权的人员接口:

    package
    com.cbf4life.common;

    /**
    *
    @author
    cbf4Life cbf4life@126.com
    * I'm glad to share my knowledge with you all.*
    父系社会,那就是男性有至高权利,handler控制权
    */

196

public interface
IHandler {//一个女性(女儿,妻子或者是母亲)要求逛街,你要处理这个请求

public void
HandleMessage(IWomen women);}

父亲、丈夫、儿子都是这个 IHandler 接口的实现者:

package
com.cbf4life.common;

/**
*
@author
cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.*
父亲
*/

public class
Father
implements
IHandler {

//未出嫁女儿来请示父亲
public void
HandleMessage(IWomen women) {

System.out.println("女儿的请示是:"+women.getRequest());

System.out.println("父亲的答复是:同意");}

}
package
com.cbf4life.common;

/**
*
@author
cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.*
丈夫类
*/

public class
Husband
implements
IHandler {

//妻子向丈夫请示
public void
HandleMessage(IWomen women) {

System.out.println("妻子的请示是:"+women.getRequest());

System.out.println("丈夫的答复是:同意");}

}

您的设计模式

197

您的设计模式

package
com.cbf4life.common;

/**
*
@author
cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.*
儿子类
*/

public class
Son
implements
IHandler {

//目前向儿子请示
public void
HandleMessage(IWomen women) {

System.out.println("母亲的请示是:"+women.getRequest());

System.out.println("儿子的答复是:同意");}

}

这三个类非常非常的简单,就一个方法,处理女儿、妻子、母亲提出的请求,再来看 Client 是怎么组装的:

package
com.cbf4life.common;import
java.util.ArrayList;

import
java.util.Random;

/**
*
@author
cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.*
我们后人来看这样的社会道德
*/

@SuppressWarnings("all")public
class
Client {

public static void
main(String[] args) {//随机挑选几个女性
Random rand =
new
Random();
ArrayList<IWomen> arrayList =
new
ArrayList();for(int
i=0;i<5;i++){

arrayList.add(new
Women(rand.nextInt(4),"我要出去逛街"));}

198

您的设计模式

//定义三个请示对象
IHandler father =
new
Father();IHandler husband =
new
Husband();IHandler son =
new
Son();

for(IWomen women:arrayList){
if(women.getType() ==1){
//未结婚少女,请示父亲

System.out.println("\n--------女儿向父亲请示-------");

father.HandleMessage(women);
}
else if(women.getType() ==2){
//已婚少妇,请示丈夫

System.out.println("\n--------妻子向丈夫请示-------");

husband.HandleMessage(women);
}
else if(women.getType() == 3){
//母亲请示儿子

System.out.println("\n--------母亲向儿子请示-------");

son.HandleMessage(women);}else{

//暂时啥也不做}

}}

}

首先是通过随机方法产生了 5 个古代妇女的对象,然后看她们是如何就逛街这件事去请示的,运行结果如下:(由于是随机的,您看到得结果可能和这里有所不同)

--------女儿向父亲请示-------女儿的请示是:我要出去逛街父亲的答复是:同意

--------妻子向丈夫请示-------妻子的请示是:我要出去逛街丈夫的答复是:同意

--------妻子向丈夫请示-------妻子的请示是:我要出去逛街丈夫的答复是:同意

--------女儿向父亲请示-------女儿的请示是:我要出去逛街父亲的答复是:同意

199

您的设计模式

“三从四德”的旧社会规范已经完整的表现出来了,你看谁向谁请示都定义出来了,但是你是不是发现这个程序写的有点不舒服?有点别扭?有点想重构它的感觉?那就对了!这段代码有以下几个问题:

失去面向对象的意义。对女儿提出的请示,应该在父亲类中做出决定,父亲这个类应该是知道女儿的请求应该自己处理,而不是在 Client 类中进行组装出来,也就是说原本应该是父亲这个类做的事情抛给了其他类进行处理;

迪米特法则相违背。我们在 Client 类中写了 if...eles 的判断条件,你看这个条件体内都是一个接口IHandler 的三个实现类,谁能处理那个请求,怎么处理,直接在实现类中定义好不就结了吗?你的类我知道的越少越好,别让我猜测你类中的逻辑,想想看,把这段 if...else 移动到三个实现类中该怎么做?

耦合过重。这个什么意思呢,我们要根据 Women 的 type 来决定使用 IHandler 的那个实现类来处理请求,我问你,如果 IHanlder 的实现类继续扩展怎么办?修改 Client 类?与开闭原则违背喽!

异常情况没有考虑。妻子只能向丈夫请示吗?如果妻子向自己的父亲请示了,父亲应该做何处理?我们的程序上可没有体现出来。

既然有这么多的问题,那我们要想办法来解决这些问题,我们可以抽象成这样一个结构,女性的请求先发送到父亲类,父亲类一看是自己要处理的,就回应处理,如果女儿已经出嫁了,那就要把这个请求转发到女婿来处理,那女婿一旦去天国报道了,那就由儿子来处理这个请求,类似于这样请求:

父亲、丈夫、儿子每个节点有两个选择:要么承担责任,做出回复;要么把请求转发到后序环节。结构分析的已经很清楚了,那我们看怎么来实现这个功能,先看类图:

200

您的设计模式

从类图上看,三个实现类 Father、Husband、Son 只要实现构造函数和父类的中抽象方法就可以了,具体怎么处理这些请求,都已经转移到了 Hanlder 抽象类中,我们来看 Hanlder 怎么实现:

package
com.cbf4life.advance;

/**
*
@author
cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.*
父系社会,那就是男性有至高权利,handler控制权
*/

public abstract class
Handler {//能处理的级别

private int
level
=0;//责任传递,下一个人责任人是谁

private
Handler
nextHanlder;

//每个类都要说明一下自己能处理哪些请求public
Handler(int
_level){

this.level
= _level;}

//一个女性(女儿,妻子或者是母亲)要求逛街,你要处理这个请求public
final void
HandleMessage(IWomen women){

if(women.getType() ==
this.level){this.response(women);

}else{
if(this.nextHanlder
!=
null){
//有后续环节,才把请求往后递送

this.nextHanlder.HandleMessage(women);}else{
//已经没有后续处理人了,不用处理了

System.out.println("-----------没地方请示了,不做处理!---------\n");
201

您的设计模式

}

}}

/*
*
如果你属于你处理的返回,你应该让她找下一个环节的人,比如

*
女儿出嫁了,还向父亲请示是否可以逛街,那父亲就应该告诉女儿,应该找丈夫请示

*/

public void
setNext(Handler _handler){this.nextHanlder
= _handler;

}

//有请示那当然要回应

public abstract void
response(IWomen women);}

有没有看到,其实在这里也用到模版方法模式,在模版方法中判断请求的级别和当前能够处理的级别,如果相同则调用基本方法,做出反馈;如果不相等,则传递到下一个环节,由下一环节做出回应。基本方法 response 要各个实现类都要实现,我们来看三个实现类:

package
com.cbf4life.advance;

/**
*
@author
cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.*
父亲
*/

public class
Father
extends
Handler {//父亲只处理女儿的请求
public
Father(){

super(1);}

//父亲的答复
@Override
public void
response(IWomen women) {

System.out.println("--------女儿向父亲请示-------");System.out.println(women.getRequest());System.out.println("父亲的答复是:同意\n");

}

202

package
com.cbf4life.advance;

/**
*
@author
cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.*
丈夫类
*/

public class
Husband
extends
Handler {//丈夫只处理妻子的请求
public
Husband(){

super(2);}

//丈夫请示的答复
@Override
public void
response(IWomen women) {

System.out.println("--------妻子向丈夫请示-------");System.out.println(women.getRequest());System.out.println("丈夫的答复是:同意\n");

}}

package
com.cbf4life.advance;

/**
*
@author
cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.*
儿子类
*/

public class
Son
extends
Handler {//儿子只处理母亲的请求
public
Son(){

super(3);}

//儿子的答复
public void
response(IWomen women) {

System.out.println("--------母亲向儿子请示-------");System.out.println(women.getRequest());System.out.println("儿子的答复是:同意\n");

}

您的设计模式

}

203

您的设计模式

}

这三个类都很简单,构造方法那是你必须实现的,父类已经定义了,子类不实现编译不通过,通过构造方法我们设置了各个类能处理的请求类型,Father 只能处理请求类型为 1(也就是女儿)的请求;Husband只能处理请求类型类 2(也就是妻子)的请求;儿子只能处理请求类型为 3(也就是目前)的请求。

Women 类的接口没有任何变化,实现类少有变化,看代码:

package
com.cbf4life.advance;

/**
*
@author
cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.*
古代女性的总称
*/

public class
Women
implements
IWomen{/*

  • *  通过一个int类型的参数来描述妇女的个人状况

  • *  1---未出嫁

  • *  2---出嫁

  • *  3---夫死

    */

    private int
    type=0;//妇女的请示

    private
    String
    request
    =
    "";

    //构造函数传递过来请求
    public
    Women(int
    _type,String _request){

    this.type
    = _type;//为了显示好看点,我在这里做了点处理

switch(this.type){

case
1:

this.request
=
"女儿的请求是:"
+ _request;

break;

case
2:

case
3:

this.request
=
"妻子的请求是:"
+ _request;

break;

this.request
=
"母亲的请求是:"
+ _request;

204

您的设计模式

}

}

//获得自己的状况
public int
getType(){

return this.type;}

//获得妇女的请求
public
String getRequest(){

return this.request;}

}

就是为了展示结果清晰一点,Women 类做了一点改变,看黄色部分。我们再来看 Client 类是怎么描述古代这一个礼节的:

package
com.cbf4life.advance;import
java.util.ArrayList;

import
java.util.Random;

/**
*
@author
cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.*
我们后人来看这样的社会道德
*/

@SuppressWarnings("all")public
class
Client {

public static void
main(String[] args) {//随机挑选几个女性
Random rand =
new
Random();
ArrayList<IWomen> arrayList =
new
ArrayList();for(int
i=0;i<5;i++){

arrayList.add(new
Women(rand.nextInt(4),"我要出去逛街"));}

//定义三个请示对象
Handler father =
new
Father();Handler husband =
new
Husband();Handler son =
new
Son();

205

您的设计模式

//设置请示顺序father.setNext(husband);husband.setNext(son);

for(IWomen women:arrayList){father.HandleMessage(women);

}}

}

通过在 Client 中设置请求的传递顺序,解决了请求到底谁来回应的问题,运行结果如下:

--------妻子向丈夫请示-------妻子的请求是:我要出去逛街丈夫的答复是:同意

--------女儿向父亲请示-------女儿的请求是:我要出去逛街父亲的答复是:同意

--------母亲向儿子请示-------母亲的请求是:我要出去逛街儿子的答复是:同意

--------妻子向丈夫请示-------妻子的请求是:我要出去逛街丈夫的答复是:同意

--------母亲向儿子请示-------母亲的请求是:我要出去逛街儿子的答复是:同意

结果也正确,业务调用类 Client 也不用去做判断到底是需要谁去处理,而且 Handler 抽象类的子类以后可以继续增加下去,只是我们这个传递链增加而已,调用类可以不用了解变化过程,甚至是谁在处理这个请求都不用知道。

以上讲解的就是责任链模式,你看 Father、Husband、Son 这三个类的处理女性的请求时是不是在传递呀,每个环节只有两个选项:要么承担责任做出回应,要么向下传递请求,最终会有环节做出回应,通用

206

您的设计模式

类图如下:

在通用类图中 Handler 是一个接口或者是抽象类,每个实现类都有两个方法 HandlerRequest 是处理请求,setNext 是设置当前环节怎么把不属于自己处理的请求扔给谁,对于这个类图我觉得需要改变,融合进来模版方法模式,类图如下:

想想单一职责法则和迪米特法则吧,通过融合模版方法模式,各个实现类只要关注的自己业务逻辑就成了,至于说什么事要自己处理,那就让父类去决定好了,也就是说父类实现了请求传递的功能,子类实现请求的处理,符合单一职责法则,各个类只作一个动作或逻辑,也就是只有一个原因引起类的改变,我建议大家在使用的时候用这种方法,好处是非常明显的了,子类的实现非常简单,责任链的建立也非常的灵活。

这里顺便插一句话,在论坛上有网友不赞成我这种写法,说是没有抓住 XX 模式的核心的,我想请问你一下,XX 模式的核心是什么?就拿今天讲的责任链模式来说,GOF 是这样说的:

207

您的设计模式

Avoid coupling the sender of a request to its receiver by giving more than one object a chanceto handle the request. Chain the receiving objects and pass the request along the chain untilan object
handles it.

这是 GOF 的原话,我想请问大侠,告诉我这句话是什么意思,如果你来给读者讲解,你想怎么讲?翻译成中文?拿着彦 X 的那本书给读者讲?照本宣科?你觉的还有人来看吗?模式是在经验中积累的,是知识的结晶,而不是死板硬套的模子!不要因为一些写模式的书比较流行就膜拜了,自己动脑筋想想这模式真是就只能是这样吗? 是不是有更优秀的方式来替代呢?别照抄别人的!

责任链模式屏蔽了请求的处理过程,你发起一个请求到底是谁处理的,这个你不用关心,只要你把请求抛给责任链的第一个处理者,最终会返回一个处理结果(当然也可以不做任何处理),作为请求者可以不用知道到底是需要谁来处理的,这是责任链模式的核心;同时责任链模式也可以做为一种补救模式来使用,举个简单例子,如项目开发的时候,需求确认是这样的:一个请求(比如银行客户存款的币种),一个处理者(只处理人民币),但是随着业务的发展(改革开放了嘛,还要处理美元、日元等等),处理者的数量和类型都有所增加,那这时候就可以在第一个处理者后面建立一个链,也就是责任链来处理请求,你是人民币,好,还是第一个业务逻辑来处理,你是美元,好,传递到第二个业务逻辑来处理,日元,欧元...,这些都不用在对原有的业务逻辑产生很大改变,通过扩展实现类就可以很好的解决这些需求变更的问题。

责任链有一个缺点是大家在开发的时候要注意:调试不是很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。

责任链在实际的项目中使用也是比较多的,我曾经做过这样一个项目,界面上有一个用户注册功能,注册用户分两种,一种是 VIP 用户,也就是在该单位办理过业务的,一种是普通用户,一个用户的注册要填写一堆信息,VIP 用户只比普通用户多了一个输入项:VIP 序列号,注册后还需要激活, VIP 和普通用户的激活流程也是不同的,VIP 是自动发送邮件到用户的邮箱中就算激活了,普通用户要发送短信才能激活,为什么呢?获得手机号码以后好发广告短信呀!这个功能项目组就采用了责任链模式,甭管从前台传递过来的是
VIP 用户信息还是普通用户信息,统一传递到一个处理入口,通过责任链来完成任务的处理,类图如下:

208

您的设计模式

其中 RegisterAction 是继承了 Strust2 中的 ActionSupport,实现 HTTP 传递过来的对象组装,组装出一个 HashMap 对象 UserInfoMap,传递给 handler 的两个实现类,具体是那个实现类来处理的,就由 HashMap上的用户标识来做决定了,这个和上面我们举的例子很类似,代码大家自己实现。

还有一个问题需要和大家说明一下,观察者模式也可以实现请求的传递,比如一个事件发生了,通知了观察者,同时观察者又作为一个被观察者,通知了另外一个观察者,这也形成了一个事件广播链,这和我们今天讲的责任链是有区别的:

受众数量不同。观察者广播链式可以 1:N 的方式广播,而责任链则要求是的 1:1 的传递,必然有一个且只有一个类完成请求的处理;

  请求内容不同。观察者广播链中的信息可以在传播中改变,但是责任链中的请求是不可改变的;

处理逻辑不通。观察者广播链主要用于触发联动动作,而责任链则是对一个类型的请求按照既定的规则进行处理。

抱歉!评论已关闭.