中国古代对妇女制定了“三从四德”的道德规范,“三从”是指“未嫁从父、既嫁从夫、夫死从子”,也就是说一个女性,在没有结婚的时候要听从于父亲,结了婚后听从于丈夫,丈夫死了还要听儿子的,举个例子来说,一个女的要出去逛街,同样这样的一个请求,在她没有出嫁前她必须征得父亲的同意,出嫁之后必须获得丈夫的许可,那丈夫死了怎么办?一般都是男的比女的死的早,还要问问儿子是否允许自己出去逛街,估计你下边马上要问要是没有儿子怎么办?请示小叔子、侄子等等,在父系社会中,妇女只占从属地位,现在想想中国的妇女还是比较悲惨的,逛个街还要请示来请示去,而且作为父亲、丈夫、儿子只有两种选择:要不承担起责任来告诉她允许或不允许逛街,要不就让她请示下一个人,这是整个社会体系的约束,应用到我们项目中就是业务规则,那我们来看怎么把“三从”通过我们的程序来实现,需求很简单:通过程序描述一下古代妇女的“三从”制度,好我们先看类图:
非常简单的类图,这个实现也非常简单,我们先看 IWomen 接口:
1 package com.pattern.chainOfResponsibility; 2 3 /** 4 * 古代悲哀女性的总称 5 * @author http://www.cnblogs.com/initial-road/ 6 * 7 */ 8 public interface IWomen { 9 10 // 获得个人状况 11 public int getType(); 12 13 //获得个人请示,你要干什么?出去逛街?约会?还是看电影 14 public String getRequest(); 15 16 }
女性就两个参数,一个是当前的个人状况,是结婚了呢还是没结婚,丈夫有没有去世,另外一个是要请示的内容,要出去逛街呀还是吃饭,我们看实现类:
1 package com.pattern.chainOfResponsibility; 2 3 /** 4 * 古代女性的总称 5 * @author http://www.cnblogs.com/initial-road/ 6 * 7 */ 8 public class Women implements IWomen{ 9 10 // 通过一个int类型的参数来描述妇女的个人状况 11 private int type = 0; 12 13 // 妇女的请示 14 private String request = ""; 15 16 // 构造函数传递过来请求 17 public Women(int _type, String _request){ 18 this.type = _type; 19 this.request = _request; 20 } 21 22 // 获得妇女的请求 23 public String getRequest() { 24 return this.request; 25 } 26 27 // 获得自己的状况 28 public int getType() { 29 return this.type; 30 } 31 32 }
我们使用数字来代表女性的不同状态,1 是未结婚,2 是已经结婚的,而且丈夫建在,3 是丈夫去世了的。我们再来看有处理权的人员接口:
1 package com.pattern.chainOfResponsibility; 2 3 /** 4 * 父系关系,那就是男性有至高权利,handler控制权 5 * @author http://www.cnblogs.com/initial-road/ 6 * 7 */ 8 public interface IHandler { 9 10 // 一个女性(女儿,妻子或者是母亲)要求逛街,你要处理这个请求 11 public void handleMessage(IWomen women); 12 13 } 14 15 // 父亲、丈夫、儿子都是这个 IHandler 接口的实现者: 16 17 package com.pattern.chainOfResponsibility; 18 19 /** 20 * 父亲 21 * @author http://www.cnblogs.com/initial-road/ 22 * 23 */ 24 public class Father implements IHandler { 25 26 // 未出嫁女儿来请求父亲 27 public void handleMessage(IWomen women) { 28 System.out.println("女儿的请求是:" + women.getRequest()); 29 System.out.println("父亲的答复是: 同意"); 30 } 31 32 } 33 34 35 package com.pattern.chainOfResponsibility; 36 37 /** 38 * 丈夫类 39 * @author http://www.cnblogs.com/initial-road/ 40 * 41 */ 42 public class Husband implements IHandler { 43 44 // 妻子向丈夫请示 45 public void handleMessage(IWomen women) { 46 System.out.println("妻子的请示是:" + women.getRequest()); 47 System.out.println("丈夫的答复是: 同意"); 48 } 49 50 } 51 52 53 package com.pattern.chainOfResponsibility; 54 55 /** 56 * 儿子类 57 * @author http://www.cnblogs.com/initial-road/ 58 * 59 */ 60 public class Son implements IHandler { 61 62 // 目前向儿子请示 63 public void handleMessage(IWomen women) { 64 System.out.println("母亲的请示是:" + women.getRequest()); 65 System.out.println("儿子的答复是: 同意"); 66 } 67 68 }
这三个类非常非常的简单,就一个方法,处理女儿、妻子、母亲提出的请求,再来看 Client 是怎么组装的:
1 package com.pattern.chainOfResponsibility; 2 3 import java.util.ArrayList; 4 import java.util.Random; 5 6 /** 7 * 我们后人来看这样的社会道德 8 * @author http://www.cnblogs.com/initial-road/ 9 * 10 */ 11 public class Client { 12 13 public static void main(String[] args) { 14 // 随机挑选几个女性 15 Random rand = new Random(); 16 ArrayList<IWomen> arrayList = new ArrayList<IWomen>(); 17 for(int i=0;i<5;i++){ 18 arrayList.add(new Women(rand.nextInt(4), "我要出去逛街")); 19 } 20 21 // 定义三个请示对象 22 IHandler father = new Father(); 23 IHandler husband = new Husband(); 24 IHandler son = new Son(); 25 26 for(IWomen women : arrayList){ 27 // 未结婚少女,请示父亲 28 if(women.getType() == 1){ 29 System.out.println("\n-----女儿向父亲请示-----\n"); 30 father.handleMessage(women); 31 }else if(women.getType() == 2){ 32 System.out.println("\n-----妻子向丈夫请示-----\n"); 33 husband.handleMessage(women); 34 }else if(women.getType() == 3){ 35 System.out.println("\n-----母亲向儿子请示-----\n"); 36 son.handleMessage(women); 37 }else { 38 // 暂时啥也不做 39 } 40 } 41 42 } 43 44 }
首先是通过随机方法产生了 5 个古代妇女的对象,她们是如何就逛街这件事去请示的(由于是随机的,您看到得结果可能和这里有所不同)。
“三从四德”的旧社会规范已经完整的表现出来了,你看谁向谁请示都定义出来了,但是你是不是发现这个程序写的有点不舒服?有点别扭?有点想重构它的感觉?那就对了!这段代码有以下几个问题:
失去面向对象的意义。对女儿提出的请示,应该在父亲类中做出决定,父亲这个类应该是知道女儿的请求应该自己处理,而不是在 Client 类中进行组装出来,也就是说原本应该是父亲这个类做的事情抛给了其他类进行处理;
迪米特法则相违背。我们在 Client 类中写了 if…eles 的判断条件,你看这个条件体内都是一个接口IHandler 的三个实现类,谁能处理那个请求,怎么处理,直接在实现类中定义好不就结了吗?你的类我知道的越少越好,别让我猜测你类中的逻辑,想想看,把这段 if…else 移动到三个实现类中该怎么做?
耦合过重。这是什么意思呢,我们要根据 Women 的 type 来决定使用 IHandler 的那个实现类来处理请求,我问你,如果 IHanlder 的实现类继续扩展怎么办?修改 Client 类?与开闭原则违背喽!
异常情况没有考虑。妻子只能向丈夫请示吗?如果妻子向自己的父亲请示了,父亲应该做何处理?我们的程序上可没有体现出来。
既然有这么多的问题,那我们要想办法来解决这些问题,我们可以抽象成这样一个结构,女性的请求先发送到父亲类,父亲类一看是自己要处理的,就回应处理,如果女儿已经出嫁了,那就要把这个请求转发到女婿来处理,那女婿一旦去天国报道了,那就由儿子来处理这个请求,类似于这样请求:
父亲、丈夫、儿子每个节点有两个选择:要么承担责任,做出回复;要么把请求转发到后序环节。结构分析的已经很清楚了,那我们看怎么来实现这个功能,先看类图:
从类图上看,三个实现类 Father、Husband、Son 只要实现构造函数和父类的中抽象方法就可以了,具体怎么处理这些请求,都已经转移到了Hanlder 抽象类中,我们来看 Hanlder 怎么实现:
1 package com.pattern.chainOfResponsibility.advance; 2 3 import com.pattern.chainOfResponsibility.IWomen; 4 5 /** 6 * 父系社会,那就是男性有至高权利,handler控制权 7 * @author http://www.cnblogs.com/initial-road/ 8 * 9 */ 10 public abstract class Handler { 11 12 // 能处理的级别 13 private int level = 0; 14 15 // 责任传递,下一个责任人是谁 16 private Handler nextHandler; 17 18 // 每个类都要说明一下自己能处理那些请求 19 public Handler(int _level){ 20 this.level = _level; 21 } 22 23 // 一个女性(女儿,妻子或者是母亲)要求逛街,你要处理这个请求 24 public final void handleMessage(IWomen women){ 25 if(women.getType() == this.level){ 26 this.response(women); 27 }else{ 28 if(this.nextHandler != null){ // 有后续环节,才把请求往后传送 29 this.nextHandler.response(women); 30 }else{ 31 // 已经没有后续处理人了,不用处理了 32 System.out.println("-----没地方请示了,不做处理了!-----\n"); 33 } 34 } 35 } 36 37 /** 38 * 如果你属于你处理的返回,你应该让她找下一个环节的人,比如 39 * 女儿出嫁了,还向父亲请示是否可以逛街,那父亲就应该告诉女儿,应该找丈夫请示 40 */ 41 public void setNext(Handler _handler){ 42 this.nextHandler = _handler; 43 } 44 45 // 有请示那当然要回应 46 public abstract void response(IWomen women); 47 }
有没有看到,其实在这里也用到模版方法模式,在模版方法中判断请求的级别和当前能够处理的级别,如果相同则调用基本方法,做出反馈;如果不相等,则传递到下一个环节,由下一环节做出回应。基本方法 response 要各个实现类都要实现,我们来看三个实现类:
1 package com.pattern.chainOfResponsibility.advance; 2 3 import com.pattern.chainOfResponsibility.IWomen; 4 5 /** 6 * 父亲 7 * @author http://www.cnblogs.com/initial-road/ 8 * 9 */ 10 public class Father extends Handler { 11 12 // 父亲只处理女儿的请求 13 public Father() { 14 super(1); 15 } 16 17 // 父亲的答复 18 public void response(IWomen women) { 19 System.out.println("-----女儿向父亲请示-----"); 20 System.out.println(women.getRequest()); 21 System.out.println("父亲的答复是: 同意\n"); 22 } 23 24 } 25 26 27 package com.pattern.chainOfResponsibility.advance; 28 29 import com.pattern.chainOfResponsibility.IWomen; 30 31 /** 32 * 丈夫类 33 * @author http://www.cnblogs.com/initial-road/ 34 * 35 */ 36 public class Husband extends Handler { 37 38 // 丈夫只处理妻子的请求 39 public Husband() { 40 super(2); 41 } 42 43 // 丈夫请示的答复 44 public void response(IWomen women) { 45 System.out.println("-----妻子向丈夫请示-----"); 46 System.out.println(women.getRequest()); 47 System.out.println("丈夫的答复是: 同意\n"); 48 } 49 50 } 51 52 53 package com.pattern.chainOfResponsibility.advance; 54 55 import com.pattern.chainOfResponsibility.IWomen; 56 57 /** 58 * 儿子类 59 * @author http://www.cnblogs.com/initial-road/ 60 * 61 */ 62 public class Son extends Handler { 63 64 // 儿子只处理母亲的请求 65 public Son() { 66 super(3); 67 } 68 69 // 儿子的答复 70 public void response(IWomen women) { 71 System.out.println("-----母亲向儿子请示-----"); 72 System.out.println(women.getRequest()); 73 System.out.println("儿子的答复是: 同意\n"); 74 } 75 76 }
这三个类都很简单,构造方法那是你必须实现的,父类已经定义了,子类不实现编译不通过,通过构造方法我们设置了各个类能处理的请求类型,Father 只能处理请求类型为 1(也就是女儿)的请求; Husband只能处理请求类型类 2(也就是妻子)的请求;儿子只能处理请求类型为 3(也就是目前)的请求。
Women 类的接口没有任何变化,实现类少有变化,看代码:
1 package com.pattern.chainOfResponsibility.advance; 2 3 import com.pattern.chainOfResponsibility.IWomen; 4 5 /** 6 * 古代女性的总称 7 * @author http://www.cnblogs.com/initial-road/ 8 * 9 */ 10 public class Women implements IWomen { 11 12 // 通过一个int类型的参数来描述妇女的个人状况 13 private int type = 0; 14 15 // 妇女的请示 16 private String request = ""; 17 18 // 构造函数传递过来请求 19 public Women(int _type, String _request){ 20 this.type = _type; 21 switch(this.type){ 22 case 1: 23 this.request = "女儿的请求是:" + _request; 24 break; 25 case 2: 26 this.request = "妻子的请求是:" + _request; 27 break; 28 case 3: 29 this.request = "母亲的请求是:" + _request; 30 break; 31 } 32 } 33 34 // 获得妇女的请求 35 public String getRequest() { 36 return this.request; 37 } 38 39 // 获得自己的状况 40 public int getType() { 41 return this.type; 42 } 43 44 }
就是为了展示结果清晰一点,Women 类做了一点改变。我们再来看 Client 类是怎么描述古代这一个礼节的:
1 package com.pattern.chainOfResponsibility.advance; 2 3 import java.util.ArrayList; 4 import java.util.Random; 5 import com.pattern.chainOfResponsibility.IWomen; 6 7 /** 8 * 我们后人来看这样的社会道德 9 * @author http://www.cnblogs.com/initial-road/ 10 * 11 */ 12 public class Client { 13 14 public static void main(String[] args) { 15 // 随机挑选几个女性 16 Random rand = new Random(); 17 ArrayList<IWomen> arrayList = new ArrayList<IWomen>(); 18 19 for(int i=0;i<5;i++){ 20 arrayList.add(new Women(rand.nextInt(4), "我要出去逛街")); 21 } 22 23 // 定义三个请示对象 24 Handler father = new Father(); 25 Handler husband = new Husband(); 26 Handler son = new Son(); 27 28 // 设置提示顺序 29 father.setNext(husband); 30 husband.setNext(son); 31 32 for(IWomen women : arrayList){ 33 father.handleMessage(women); 34 } 35 } 36 37 }
结果也正确,业务调用类 Client 也不用去做判断到底是需要谁去处理,而且 Handler 抽象类的子类以后可以继续增加下去,只是我们这个传递链增加而已,调用类可以不用了解变化过程,甚至是谁在处理这个请求都不用知道。
以上讲解的就是责任链模式,你看 Father、Husband、Son 这三个类的处理女性的请求时是不是在传递呀,每个环节只有两个选项:要么承担责任做出回应,要么向下传递请求,最终会有环节做出回应,通用类图如下:
在通用类图中 Handler 是一个接口或者是抽象类,每个实现类都有两个方法 HandlerRequest 是处理请求,setNext 是设置当前环节怎么把不属于自己处理的请求扔给谁,对于这个类图我觉得需要改变,融合进来模版方法模式,类图如下:
想想单一职责法则和迪米特法则吧,通过融合模版方法模式,各个实现类只要关注的自己业务逻辑就成了,至于说什么事要自己处理,那就让父类去决定好了,也就是说父类实现了请求传递的功能,子类实现请求的处理,符合单一职责法则,各个类只作一个动作或逻辑,也就是只有一个原因引起类的改变,我 建议大家在使用的时候用这种方法,好处是非常明显的了,子类的实现非常简单,责任链的建立也非常的灵活。