责任链模式例子:中国古代女子有“三从四德”的道德规范,就是说一个女性,在没有结婚的时候听父亲的,结婚了之后听丈夫的,丈夫死了听儿子的。要是想出去逛街了,需要请示。作为父亲、丈夫、儿子只有两种选择:要不承担起责任来告诉她允许或不允许逛街,要不就让她请示下一个人。我们来看怎么把“三从”通过我们的程序来实现,需求很简单:通过程序描述一下古代妇女的“三从制度”,好,我们老看类图:
非常简单的类图,这个实现也非常简单,我们先看IWomen接口:
package com.nextvpu.myapplication;
/**
* Created by NEXTVPU on 2018/6/16.
* 古代悲哀女性的总称
*/
public interface IWomen {
//获得个人状况
public int getType();
//获得个人请示
public String getRequest();
}
女性就两个参数,一个是当前的个人状况,是结婚了呢还是没结婚,丈夫有没有去世,另外一个要请示的内容,要出去逛街啊还是吃饭,我们来看类图:
package com.nextvpu.myapplication;
/**
* Created by NEXTVPU on 2018/6/16.
* 古代女性的总称
*/
public class Women implements IWomen {
/**
* 通过一个int类型的参数来描述妇女的个人状况
* 1--未出嫁
* 2--出嫁
* 3--夫死
* @return
*/
private int type = 0;
//妇女的请示
private String request = "";
@Override
public int getType() {
return this.type;
}
//构造函数传递过来请求
public Women(int _type,String _request){
this.type = _type;
//为了显示好看点,我在这里做了点处理
switch (this.type){
case 1:
this.request = "女儿的请求是:"+_request;
break;
case 2:
this.request = "妻子的请求时:"+_request;
break;
case 3:
this.request = "母亲的请求是:"+_request;
break;
}
}
@Override
public String getRequest() {
return this.request;
}
}
我们使用数字来代表女性的不同状态,1 是为结婚 2 是已结婚,而且丈夫健在 3 丈夫去世了,儿子在
我们再来看有处理权的人员接口:
package com.nextvpu.myapplication;
/**
* Created by NEXTVPU on 2018/6/16.
* 父系社会,那就是男性有至高权利,handler控制权
*/
public interface IHandler {
//一个女性(女儿,妻子或者是母亲)要求逛街,你要处理这个请求
public void HandleMessage(IWomen women);
}
父亲、丈夫、儿子都是这个IHander接口的实现者:
package com.nextvpu.myapplication;
import android.util.Log;
/**
* Created by NEXTVPU on 2018/6/16.
* 父亲
*/
public class Father implements IHandler {
//未出嫁女儿来请示父亲
@Override
public void HandleMessage(IWomen women) {
Log.e("xyz","女儿的请示是:"+women.getRequest());
Log.e("xyz","父亲的答复是:,同意");
}
}
package com.nextvpu.myapplication;
import android.util.Log;
/**
* Created by NEXTVPU on 2018/6/16.
* 丈夫类
*/
public class Husband implements IHandler {
//妻子请示丈夫
@Override
public void HandleMessage(IWomen women) {
Log.e("xyz","妻子的请示是:"+women.getRequest());
Log.e("xyz","丈夫的答复是:,同意");
}
}
package com.nextvpu.myapplication;
import android.util.Log;
/**
* Created by NEXTVPU on 2018/6/16.
* 儿子类
*/
public class Son implements IHandler {
//目前向儿子请示
@Override
public void HandleMessage(IWomen women) {
Log.e("xyz","母亲的请示是:"+women.getRequest());
Log.e("xyz","儿子的答复是:,同意");
}
}
这三个类非常非常简单,就一个方法,处理女儿、妻子、母亲提出的请求,再来看Client是怎么组装的:
private void Client() {
//随机挑选几个女性
Random rand = new Random();
ArrayList<IWomen> arrayList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
arrayList.add(new Women(rand.nextInt(4),"我要出去逛街"));
}
//定义三个请示对象
IHandler father = new Father();
IHandler husband = new Husband();
IHandler son = new Son();
for (IWomen women:arrayList
) {
if (women.getType()==1){//未婚少女,请示父亲
Log.e("xyz","\n--------女儿向父亲请示----");
father.HandleMessage(women);
}else if (women.getType()==2){
Log.e("xyz","\n--------妻子向丈夫请示----");
husband.HandleMessage(women);
}else if (women.getType()==3){
Log.e("xyz","\n--------母亲向儿子请示----");
son.HandleMessage(women);
}else{
//暂时啥都不做
}
}
}
“三从四德”的旧社会规范已经完整的表现出来了,但是有问题:
失去面对对象的意义。对女儿提出的请示,应该在父亲类中做出决定,父亲这个类应该是知道女儿的请求应该自己处理,而不是在Client类中进行组装出来,也就是说原本应该是父亲这个类做的事情抛给了其他类进行处理;
迪米特法则相违背。我们在Client类中写了if…selse的判断条件,你看这个条件内都有一个接口IHandler的三个实现类,谁能处理那个请求,怎么处理,直接在实现类中定义好不就结了吗?你的类我知道的越少越好,别让我猜测你类中的逻辑,想想看,把这段if…else移动到三个实现类中该怎么做?
耦合过度。我们要根据Women的type来决定使用IHandler的那个实现类来处理请求,我问你,如果IHandler的实现类继续扩展在呢么办?修改Client类?与开闭原则违背咯。
异常情况没有考虑。妻子只向丈夫请示吗?如果妻子向自己丈夫请示了,父亲应该做何处理呢?我们的程序上可没有体现出来。
既然有那么多的问题,那我们要想办法来解决这些问题,我们可以抽象成这样一个结构,女性的请求先发送到父亲类,父亲类一看是自己要处理的,就回应处理,如果女儿已经出嫁了,那就把请求转发到女婿这里来处理,那女婿要是不在了,那就由儿子来处理这个请求,类似于这样请求:
父亲、丈夫、儿子每个节点有两个选择:要么承担责任,做出回复;要么把请求转发到后序环节。结构分析已经很清楚了,那我们看怎么来实现这个功能,先看类图:
从类图上看,三个实现类Father、Husband、Son只要实现构造函数和父类中抽象方法就可以了,具体怎么处理这些请求,都已经转移到Handler抽象类中,我们来看handler怎么实现:
package com.nextvpu.myapplication;
import android.util.Log;
/**
* Created by NEXTVPU on 2018/6/16.
* 父系社会,那就是男性有至高权利,handler控制权
*/
public abstract class Handler {
//能处理的级别
private int level = 0;
//责任传递,下一个责任人是谁
private Handler nextHandler;
//每个类都要说明一下自己能处理哪些请求
public Handler(int _level){
this.level = _level;
}
//一个女性要求逛街,你要处理这个请求
public final void HandleMessage(IWomen women){
if (women.getType() == this.level){
this.response(women);
}else{
if (this.nextHandler!=null){//有后续环节,才把请求往后递送
this.nextHandler.HandleMessage(women);
}else {//已经没有后续处理人了,不用处理
Log.e("xyz","没地方请示了,不做处理!-----\n");
}
}
}
/**
* 如果你属于你处理的返回,你应该让她找下一个环节的人,比如
* 女儿出嫁了,还向父亲请示是否可以逛街,那父亲就应该告诉女儿,应该找丈夫请示
* @param
*/
public void setNext(Handler _handler){
this.nextHandler = _handler;
}
//有请示那当然要回应
public abstract void response(IWomen women);
}
有没有看到,其实这里也用到模版方法模式,在模版方法中判断请求的级别和当前能够处理的级别,如果相同则调用基本方法,作出反馈了如果不想等,则传递到下一个环节,有下一个环节做出回应,基本方法response要哥哥实现类都要实现,我们来看三个实现类:
package com.nextvpu.myapplication;
import android.util.Log;
/**
* Created by NEXTVPU on 2018/6/16.
* 父亲
*/
public class FatherChange extends Handler {
//父亲只处理女儿的请求
public FatherChange(){
super(1);
}
@Override
public void response(IWomen women) {
Log.e("xyz"," ---- 女儿想父亲请示 ---- ");
Log.e("xyz"," women.getRequest() "+women.getRequest());
Log.e("xyz"," 父亲的答复是:同意\n ");
}
}
package com.nextvpu.myapplication;
import android.util.Log;
/**
* Created by NEXTVPU on 2018/6/16.
* 父亲
*/
public class HusbandChange extends Handler {
//父亲只处理女儿的请求
public HusbandChange(){
super(2);
}
@Override
public void response(IWomen women) {
Log.e("xyz"," ---- 妻子向丈夫请示 ---- ");
Log.e("xyz"," women.getRequest() "+women.getRequest());
Log.e("xyz"," 丈夫的答复是:同意\n ");
}
}
package com.nextvpu.myapplication;
import android.util.Log;
/**
* Created by NEXTVPU on 2018/6/16.
* 父亲
*/
public class SonChange extends Handler {
//父亲只处理女儿的请求
public SonChange(){
super(3);
}
@Override
public void response(IWomen women) {
Log.e("xyz"," ---- 妻子向儿子请示 ---- ");
Log.e("xyz"," women.getRequest() "+women.getRequest());
Log.e("xyz"," 儿子的答复是:同意\n ");
}
}
这三个类都很简单,都早方法那是你必须实现的,父类已经定义了,子类不实现编译不通过,通构造方法我们设置了哥哥类能处理的请求类型,Father只能处理请求类型为1(也就是女儿)的请求;Husband只能处理请求类型为2(也就是妻子)的请求;儿子只能处理请求类型为3(也就是目前)的请求。
Women类的接口没有任何变化,实现类稍微修改,看代码:
package com.nextvpu.myapplication;
/**
* Created by NEXTVPU on 2018/6/16.
* 古代女性的总称
*/
public class Women implements IWomen {
/**
* 通过一个int类型的参数来描述妇女的个人状况
* 1--未出嫁
* 2--出嫁
* 3--夫死
* @return
*/
private int type = 0;
//妇女的请示
private String request = "";
@Override
public int getType() {
return this.type;
}
//构造函数传递过来请求
public Women(int _type,String _request){
this.type = _type;
//为了显示好看点,我在这里做了点处理
switch (this.type){
case 1:
this.request = "女儿的请求是:"+_request;
break;
case 2:
this.request = "妻子的请求时:"+_request;
break;
case 3:
this.request = "母亲的请求是:"+_request;
break;
}
}
@Override
public String getRequest() {
return this.request;
}
}
就是为了展示结果清晰一些,Women类做了一点改变,我们看最终实现类怎么描述:
private void ClientChange() {
//随机挑选几个女性
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 FatherChange();
Handler husband = new HusbandChange();
Handler son = new SonChange();
//设置请示顺序
father.setNext(husband);
husband.setNext(son);
for (IWomen women:arrayList
) {
father.HandleMessage(women);
}
}
结果也正确,业务调用类ClientChnage也不用去判断到底是需要谁去处理,而且Handler抽象类的子类可以继续增加下去,只是我们这个传递链增加而已,调用类可以不用了解变化过程,甚至是谁在处理这个请求都不用知道。
以上讲解的就是责任链模式,你看Father、Husband、Son这三个类的处理女性的请求时是不是在传递呢,每个环节只有两个选项要么承担责任做出回应,要么向下传递请求,最终会有环节做出回应通用类图如下:
在通用类图中Handler是一个接口或者抽象类,每个抽象类都有两个方法HandlerRequest是处理请求,setNext是设置当前环节怎么把不属于自己处理的请求扔给谁,对于这个类图我觉得需要改变,融合来模版方法模式,类图如下:
想一想单一职责法则和迪米特法则吧,通过融合模版方法模式,各个实现类只要关注的自己业务逻辑就成了,至于说什么事要自己处理,那就让父类去决定好了,也就是说父类实现了请求传递的功能,子类实现请求的处理,符合单一职责法则,各个类只做一个动作或逻辑,也就是只有一个原因引起类的改变,我建议大家在使用的时候用这种方法,好处非常明显的了,子类实现非常简单,责任链的建立也非常的灵活。
责任链有一个缺点是大家开发的时候要注意:调试不是很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。
还有一个问题需要和大家说明一下,观察者模式也可以实现请求的传递,比如一个事件发生了,通知了观察者,同事观察者有作为一个被观察者,通知了另外一个观察者,这也形成了一个事件广播链,这个我们今天讲的责任链是有区别的:
受众数量不同。观察者广播链可以1:N的方式广播,而责任链要求是1:1的传递,必然有一个且只有一个类完成请求的处理;
请求内容不同。观察者广播链中的信息可以在传播中改变,但是责任链中的请求是不可改变的
处理逻辑不同。观察者广播链主要用于出发联动动作,而责任链则是对一个类型的请求按照既定的规则进行处理。
责任链模式DEMO下载地址:
demo下载