第16章 责任链模式
16.1 古代妇女的枷锁——“三从四德”
中国古代对妇女制定了“三从四德”的道德规范,“三从”是指“未嫁从父、既嫁从夫、夫死从子”。也就是说,一位女性在结婚之前要听从于父亲,结婚之后要听从于丈夫,如果丈夫死了还要听从于儿子。举例来说,如果一位女性要出去逛街,在她出嫁前必须征得父亲的同意,出嫁之后必须获得丈夫的许可,那丈夫死了怎么办?那就得问问儿子是否允许自己出去逛街。估计你接下来马上要问:“要是没有儿子怎么办?”那就请示小叔子、侄子等。在父系社会中,妇女只占从属地位,现在想想中国古代的妇女还是挺悲惨的,连逛街都要多番请示。作为父亲、丈夫或儿子,只有两种选择:要不承担起责任来,允许她或不允许她逛街;要不就让她请示下一个人,这是整个社会体系的约束,应用到我们项目中就是业务规则。下面来看如何通过程序来实现“三从”,需求很简单:通过程序描述一下古代妇女的“三从”制度。好,我们先来看类图,如图16-1所示。
![](https://box.kancloud.cn/2016-08-14_57b003649ba88.jpg)
图16-1 妇女“三从”类图
类图非常简单,IHandler是三个有决策权对象的接口,IWomen是女性的代码,其实现也非常简单,IWomen如代码清单16-1所示。
代码清单16-1 女性接口
public interface IWomen {
//获得个人状况
public int getType();
//获得个人请示,你要干什么?出去逛街?约会?还是看电影?
public String getRequest();
}
女性接口仅两个方法,一个是取得当前的个人状况getType,通过返回值决定是结婚了还是没结婚、丈夫是否在世等,另外一个方法getRequest是要请示的内容,要出去逛街还是吃饭,其实现类如代码清单16-2所示。
代码清单16-2 古代妇女
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是丈夫去世了。从整个设计上分析,有处理权的人(如父亲、丈夫、儿子)才是设计的核心,他们是要处理这些请求的,我们来看有处理权的人员接口IHandler,如代码清单16-3所示。
代码清单16-3 有处理权的人员接口
public interface IHandler {
//一个女性(女儿、妻子或者母亲)要求逛街,你要处理这个请求
public void HandleMessage(IWomen women);
}
非常简单,有处理权的人对妇女的请求进行处理,分别有三个实现类,在女儿没有出嫁之前父亲是有决定权的,其实现类如代码清单16-4所示。
代码清单16-4 父亲类
public class Father implements IHandler {
//未出嫁的女儿来请示父亲
public void HandleMessage(IWomen women) {
System.out.println("女儿的请示是:"+women.getRequest());
System.out.println("父亲的答复是:同意");
}
}
在女性出嫁后,丈夫有决定权,如代码清单16-5所示。
代码清单16-5 丈夫类
public class Husband implements IHandler {
//妻子向丈夫请示
public void HandleMessage(IWomen women) {
System.out.println("妻子的请示是:"+women.getRequest());
System.out.println("丈夫的答复是:同意");
}
}
在女性丧偶后,对母亲提出的请求儿子有决定权,如代码清单16-6所示。
代码清单16-6 儿子类
public class Son implements IHandler {
//母亲向儿子请示
public void HandleMessage(IWomen women) {
System.out.println("母亲的请示是:"+women.getRequest());
System.out.println("儿子的答复是:同意");
}
}
以上三个实现类非常简单,只有一个方法,处理女儿、妻子、母亲提出的请求,我们来模拟一下一个古代妇女出去逛街是如何请示的,如代码清单16-7所示。
代码清单16-7 场景类
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),"我要出去逛街"));
}
//定义三个请示对象
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个古代妇女的对象,然后看她们是如何就逛街这件事去请示的,运行结果如下所示(由于是随机的,您看到的结果可能和这里有所不同):
--------女儿向父亲请示-------
女儿的请示是:我要出去逛街
父亲的答复是:同意
--------母亲向儿子请示-------
母亲的请示是:我要出去逛街
儿子的答复是:同意
--------妻子向丈夫请示-------
妻子的请示是:我要出去逛街
丈夫的答复是:同意
--------女儿向父亲请示-------
女儿的请示是:我要出去逛街
父亲的答复是:同意
“三从四德”的旧社会规范已经完整地表现出来了,你看谁向谁请示都定义出来了,但是你是不是发现这个程序写得有点不舒服?有点别扭?有点想重构它的感觉?那就对了!这段代码有以下几个问题:
● 职责界定不清晰
对女儿提出的请示,应该在父亲类中做出决定,父亲有责任、有义务处理女儿的请示,因此Father类应该是知道女儿的请求自己处理,而不是在Client类中进行组装出来,也就是说原本应该是父亲这个类做的事情抛给了其他类进行处理,不应该是这样的。
● 代码臃肿
我们在Client类中写了if...else的判断条件,而且能随着能处理该类型的请示人员越多,if...else的判断就越多,想想看,臃肿的条件判断还怎么有可读性?!
● 耦合过重
这是什么意思呢,我们要根据Women的type来决定使用IHandler的那个实现类来处理请求。有一个问题是:如果IHandler的实现类继续扩展怎么办?修改Client类?与开闭原则违背了!
● 异常情况欠考虑
妻子只能向丈夫请示吗?如果妻子(比如一个现代女性穿越到古代了,不懂什么“三从四德”)向自己的父亲请示了,父亲应该做何处理?我们的程序上可没有体现出来,逻辑失败了!
既然有这么多的问题,那我们要想办法来解决这些问题,我们先来分析一下需求,女性提出一个请示,必然要获得一个答复,甭管是同意还是不同意,总之是要一个答复的,而且这个答复是唯一的,不能说是父亲作出一个决断,而丈夫也作出了一个决断,也即是请示传递出去,必然有一个唯一的处理人给出唯一的答复,OK,分析完毕,收工,重新设计,我们可以抽象成这样一个结构,女性的请求先发送到父亲类,父亲类一看是自己要处理的,就作出回应处理,如果女儿已经出嫁了,那就要把这个请求转发到女婿来处理,那女婿一旦去天国报道了,那就由儿子来处理这个请求,类似于如图16-2所示的顺序处理图。
![](https://box.kancloud.cn/2016-08-14_57b00364b141a.jpg)
图16-2 女性请示的顺序处理图
父亲、丈夫、儿子每个节点有两个选择:要么承担责任,做出回应;要么把请求转发到后序环节。结构分析得已经很清楚了,那我们看怎么来实现这个功能,类图重新修正,如图16-3所示。
![](https://box.kancloud.cn/2016-08-14_57b00364c8cd3.jpg)
图16-3 顺序处理的类图
从类图上看,三个实现类Father、Husband、Son只要实现构造函数和父类中的抽象方法response就可以了,具体由谁处理女性提出的请求,都已经转移到了Handler抽象类中,我们来看Handler怎么实现,如代码清单16-8所示。
代码清单16-8 修改后的Handler类
public abstract class Handler {
public final static int FATHER_LEVEL_REQUEST = 1;
public final static int HUSBAND_LEVEL_REQUEST = 2;
public final static int SON_LEVEL_REQUEST = 3;
//能处理的级别
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{ //已经没有后续处理人了,不用处理了
System.out.println("---没地方请示了,按不同意处理---\n");
}
}
}
/*
* 如果不属于你处理的请求,你应该让她找下一个环节的人,如女儿出嫁了,
* 还向父亲请示是否可以逛街,那父亲就应该告诉女儿,应该找丈夫请示
*/
public void setNext(Handler _handler){
this.nextHandler = _handler;
}
//有请示那当然要回应
protected abstract void response(IWomen women);
}
方法比较长,但是还是比较简单的,读者有没有看到,其实在这里也用到模板方法模式,在模板方法中判断请求的级别和当前能够处理的级别,如果相同则调用基本方法,做出反馈;如果不相等,则传递到下一个环节,由下一环节做出回应,如果已经达到环节结尾,则直接做不同意处理。基本方法response需要各个实现类实现,每个实现类只要实现两个职责:一是定义自己能够处理的等级级别;二是对请求做出回应,我们首先来看首节点Father类,如代码清单16-9所示。
代码清单16-9 父亲类
public class Father extends Handler {
//父亲只处理女儿的请求
public Father(){
super(Handler.FATHER_LEVEL_REQUEST);
}
//父亲的答复
protected void response(IWomen women) {
System.out.println("--------女儿向父亲请示-------");
System.out.println(women.getRequest());
System.out.println("父亲的答复是:同意\n");
}
}
丈夫类定义自己能处理的等级为2的请示,如代码清单16-10所示。
代码清单16-10 丈夫类
public class Husband extends Handler {
//丈夫只处理妻子的请求
public Husband(){
super(Handler.HUSBAND_LEVEL_REQUEST);
}
//丈夫请示的答复
protected void response(IWomen women) {
System.out.println("--------妻子向丈夫请示-------");
System.out.println(women.getRequest());
System.out.println("丈夫的答复是:同意\n");
}
}
儿子类只能处理等级为3的请示,如代码清单16-11所示。
代码清单16-11 儿子类
public class Son extends Handler {
//儿子只处理母亲的请求
public Son(){
super(Handler.SON_LEVEL_REQUEST);
}
//儿子的答复
protected void response(IWomen women) {
System.out.println("--------母亲向儿子请示-------");
System.out.println(women.getRequest());
System.out.println("儿子的答复是:同意\n");
}
}
这三个类都很简单,构造方法是必须实现的,父类框定子类必须有一个显式构造函数,子类不实现编译不通过。通过构造方法我们设置了各个类能处理的请求类型,Father只能处理请求类型为1(也就是女儿)的请求;Husband只能处理请求类型类为2(也就是妻子)的请求,儿子只能处理请求类型为3(也就是母亲)的请求,那如果请求类型为4的该如何处理呢?在Handler中我们已经判断了,如何没有相应的处理者(也就是没有下一环节),则视为不同意。
Women类的接口没有任何变化,请参考图16-1所示。
实现类稍微有些变化,如代码清单16-12所示。
代码清单16-12 女性类
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:
this.request = "妻子的请求是:" + _request;
break;
case 3:
this.request = "母亲的请求是:" + _request;
}
}
//获得自己的状况
public int getType(){
return this.type;
}
//获得妇女的请求
public String getRequest(){
return this.request;
}
}
为了展示结果清晰一点,Women类做了一些改变,如粗体部分所示。我们再来看Client类是怎么描述古代这一个礼节的,如代码清单16-13所示。
代码清单16-13 场景类
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();
//设置请示顺序
father.setNext(husband);
husband.setNext(son);
for(IWomen women:arrayList){
father.HandleMessage(women);
}
}
}
在Client中设置请求的传递顺序,先向父亲请示,不是父亲应该解决的问题,则由父亲传递到丈夫类解决,若不是丈夫类解决的问题则传递到儿子类解决,最终的结果必然有一个返回,其运行结果如下所示。
--------妻子向丈夫请示-------
妻子的请求是:我要出去逛街
丈夫的答复是:同意
--------女儿向父亲请示-------
女儿的请求是:我要出去逛街
父亲的答复是:同意
--------母亲向儿子请示-------
母亲的请求是:我要出去逛街
儿子的答复是:同意
--------妻子向丈夫请示-------
妻子的请求是:我要出去逛街
丈夫的答复是:同意
--------母亲向儿子请示-------
母亲的请求是:我要出去逛街
儿子的答复是:同意
结果也正确,业务调用类Client也不用去做判断到底是需要谁去处理,而且Handler抽象类的子类可以继续增加下去,只需要扩展传递链而已,调用类可以不用了解变化过程,甚至是谁在处理这个请求都不用知道。在这种模式下,即使现代社会的一个小太妹穿越到古代(例如掉入时空隧道,或者时空突然扭转,甚至是突然魔法显灵),对“三从四德”没有任何了解也可以自由地应付,反正只要请示父亲就可以了,该父亲处理就父亲处理,不该父亲处理就往下传递。这就是责任链模式。
- 前言
- 第一部分 大旗不挥,谁敢冲锋——6大设计原则全新解读
- 第1章 单一职责原则
- 1.2 绝杀技,打破你的传统思维
- 1.3 我单纯,所以我快乐
- 1.4 最佳实践
- 第2章 里氏替换原则
- 2.2 纠纷不断,规则压制
- 2.3 最佳实践
- 第3章 依赖倒置原则
- 3.2 言而无信,你太需要契约
- 3.3 依赖的三种写法
- 3.4 最佳实践
- 第4章 接口隔离原则
- 4.2 美女何其多,观点各不同
- 4.3 保证接口的纯洁性
- 4.4 最佳实践
- 第5章 迪米特法则
- 5.2 我的知识你知道得越少越好
- 5.3 最佳实践
- 第6章 开闭原则
- 6.2 开闭原则的庐山真面目
- 6.3 为什么要采用开闭原则
- 6.4 如何使用开闭原则
- 6.5 最佳实践
- 第二部分 真刀实枪 ——23种设计模式完美演绎
- 第7章 单例模式
- 7.2 单例模式的定义
- 7.3 单例模式的应用
- 7.4 单例模式的扩展
- 7.5 最佳实践
- 第8章 工厂方法模式
- 8.2 工厂方法模式的定义
- 8.3 工厂方法模式的应用
- 8.4 工厂方法模式的扩展
- 8.5 最佳实践
- 第9章 抽象工厂模式
- 9.2 抽象工厂模式的定义
- 9.3 抽象工厂模式的应用
- 9.4 最佳实践
- 第10章 模板方法模式
- 10.2 模板方法模式的定义
- 10.3 模板方法模式的应用
- 10.4 模板方法模式的扩展
- 10.5 最佳实践
- 第11章 建造者模式
- 11.2 建造者模式的定义
- 11.3 建造者模式的应用
- 11.4 建造者模式的扩展
- 11.5 最佳实践
- 第12章 代理模式
- 12.2 代理模式的定义
- 12.3 代理模式的应用
- 12.4 代理模式的扩展
- 12.5 最佳实践
- 第13章 原型模式
- 13.2 原型模式的定义
- 13.3 原型模式的应用
- 13.4 原型模式的注意事项
- 13.5 最佳实践
- 第14章 中介者模式
- 14.2 中介者模式的定义
- 14.3 中介者模式的应用
- 14.4 中介者模式的实际应用
- 14.5 最佳实践
- 第15章 命令模式
- 15.2 命令模式的定义
- 15.3 命令模式的应用
- 15.4 命令模式的扩展
- 15.5 最佳实践
- 第16章 责任链模式
- 16.2 责任链模式的定义
- 16.3 责任链模式的应用
- 16.4 最佳实践
- 第17章 装饰模式
- 17.2 装饰模式的定义
- 17.3 装饰模式应用
- 17.4 最佳实践
- 第18章 策略模式
- 18.2 策略模式的定义
- 18.3 策略模式的应用
- 18.4 策略模式的扩展
- 18.5 最佳实践
- 第19章 适配器模式
- 19.2 适配器模式的定义
- 19.3 适配器模式的应用
- 19.4 适配器模式的扩展
- 19.5 最佳实践
- 第20章 迭代器模式
- 20.2 迭代器模式的定义
- 20.3 迭代器模式的应用
- 20.4 最佳实践
- 第21章 组合模式
- 21.2 组合模式的定义
- 21.3 组合模式的应用
- 21.4 组合模式的扩展
- 21.5 最佳实践
- 第22章 观察者模式
- 22.2 观察者模式的定义
- 22.3 观察者模式的应用
- 22.4 观察者模式的扩展
- 22.5 最佳实践
- 第23章 门面模式
- 23.2 门面模式的定义
- 23.3 门面模式的应用
- 23.4 门面模式的注意事项
- 23.5 最佳实践
- 第24章 备忘录模式
- 24.2 备忘录模式的定义
- 24.3 备忘录模式的应用
- 24.4 备忘录模式的扩展
- 24.5 最佳实践
- 第25章 访问者模式
- 25.2 访问者模式的定义
- 25.3 访问者模式的应用
- 25.4 访问者模式的扩展
- 25.5 最佳实践
- 第26章 状态模式
- 26.2 状态模式的定义
- 26.3 状态模式的应用
- 第27章 解释器模式
- 27.2 解释器模式的定义
- 27.3 解释器模式的应用
- 27.4 最佳实践
- 第28章 享元模式
- 28.2 享元模式的定义
- 28.3 享元模式的应用
- 28.4 享元模式的扩展
- 28.5 最佳实践
- 第29章 桥梁模式
- 29.2 桥梁模式的定义
- 29.3 桥梁模式的应用
- 29.4 最佳实践
- 第三部分 谁的地盘谁做主 ——设计模式PK
- 第30章 创建类模式大PK
- 30.1 工厂方法模式VS建造者模式
- 30.2 抽象工厂模式VS建造者模式
- 第31章 结构类模式大PK
- 31.1 代理模式VS装饰模式
- 31.2 装饰模式VS适配器模式
- 第32章 行为类模式大PK
- 32.1 命令模式VS策略模式
- 32.2 策略模式VS状态模式
- 32.3 观察者模式VS责任链模式
- 第33章 跨战区PK
- 33.1 策略模式VS桥梁模式
- 33.2 门面模式VS中介者模式
- 33.3 包装模式群PK
- 第四部分 完美世界 ——设计模式混编
- 第34章 命令模式+责任链模式
- 34.2 混编小结
- 第35章 工厂方法模式+策略模式
- 35.2 混编小结
- 第36章 观察者模式+中介者模式
- 36.2 混编小结
- 第五部分 扩展篇
- 第37章 MVC框架
- 37.2 最佳实践
- 第38章 新模式
- 38.1 规格模式
- 38.2 对象池模式
- 38.3 雇工模式
- 38.4 黑板模式
- 38.5 空对象模式
- 附录 23种设计模式彩图