北风设计模式课程---责任链模式 总结
一、总结
一句话总结:
设置了级别的场景:责任链适合那些已经设置了级别的场景,
都有对应的抽象类:面向对象这些设计模式,处理类或者工厂类都有对应的抽象类,比如这里责任链模式的 处理类的抽象父类,作用自然是满足 开闭原则。
1、责任链模式 比正常例子可爱一点的 例子?
古代女子逛街请示:古代女子有“三从四德”之说,“三从”即指“未嫁从父、既嫁从夫、夫死从子”。举例来说,如果一位女性要出去逛街,在她出嫁前必须征得父亲的同意,出嫁之后必须获得丈夫的许可,那丈夫死了怎么办?那就得问问儿子是否允许自己出去逛街。
古代女子有“三从四德”之说,“三从”即指“未嫁从父、既嫁从夫、夫死从子”。也就是说,一位女性在结婚之前要听从于父亲,结婚之后要听从于丈夫,如果丈夫死了还要听从于儿子。举例来说,如果一位女性要出去逛街,在她出嫁前必须征得父亲的同意,出嫁之后必须获得丈夫的许可,那丈夫死了怎么办?那就得问问儿子是否允许自己出去逛街。估计你接下来马上要问:“要是没有儿子怎么办?”那就请示小叔子、侄子等。在父系社会中,妇女只占从属地位,现在想想中国古代的妇女还是挺悲惨的,连逛街都要多番请示。作为父亲、丈夫或儿子,只有两种选择:要不承担起责任来,允许她或不允许她逛街;要不就让她请示下一个人,这是整个社会体系的约束,应用到我们项目中就是业务规则。
2、如下设计解决责任链模式的方法的缺点是什么?
|||-begin
class Client { public static void main(String[] args) { // 随机挑选几个女性 Random rand = new Random(); ArrayList<IWoman> arrayList = new ArrayList(); for (int i = 0; i < 5; i++) { arrayList.add(new Woman(rand.nextInt(4), "我要出去逛街")); } // 定义三个请示对象 IHandler father = new Father(); IHandler husband = new Husband(); IHandler son = new Son(); for (IWoman woman : arrayList) { if (woman.getType() == 1) { // 未结婚少女, 请示父亲 System.out.println(" --------女儿向父亲请示-------"); father.HandleMessage(woman); } else if (woman.getType() == 2) { // 已婚女子, 请示丈夫 System.out.println(" --------妻子向丈夫请示-------"); husband.HandleMessage(woman); } else if (woman.getType() == 3) { // 母亲请示儿子 System.out.println(" --------母亲向儿子请示-------"); son.HandleMessage(woman); } else { // 暂时什么也不做 } } } }
|||-end
职责界定不清晰:对女儿提出的请示,应该在父亲类中做出决定,因此 Father 类应该是知道女儿的请求自己处理,而不是在 Client 类中进行组装,不应该是这样的。
代码臃肿:我们在 Client 类中写了很多 if...else 的判断条件,而且能随着能处理该类型的请示人员越多,if...else 的判断就越多,代码就越来越臃肿,可读性就越来越低。
耦合严重:这是什么意思呢,我们要根据 Woman 的 type 来决定使用 IHandler 的那个实现类来处理请求。有一个问题是:如果 IHandler 的实现类继续扩展怎么办?修改 Client 类?这与开闭原则违背。
异常情况欠考虑:女子提出一个请示,必然要获得一个答复,甭管是同意还是不同意,总之是要一个答复的,而且这个答复是唯一的,不能说是父亲作出一个决断,而丈夫也作出了一个决断,正确的顺序应该是请求一层一层的传递的,父亲处理过后再由丈夫处理。
3、责任链抽象Handle类实例?
|||-begin
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(IWoman woman) { if (woman.getType() == this.level) { this.response(woman); } else { if (this.nextHandler != null) { // 有后续环节, this.nextHandler.HandleMessage(woman); // 才把请求往后递送this.nextHandler.HandleMessage(women); } else { // 已经没有后续处理人了, 不用处理了 System.out.println("---没地方请示了, 按不同意处理--- "); } } } // 如果不属于你处理的请求, 你应该让她找下一个环节的人, 如女儿出嫁了, 还向父亲请示是否可以逛街, 那父亲就应该告诉女儿, 应该找丈夫请示 public void setNext(Handler _nextHandler) { this.nextHandler = _nextHandler; } // 有请示那当然要回应 protected abstract void response(IWoman woman); }
|||-end
有级别 ,有处理请求,有找自己的下一级
找下一级的操作就是:设置(获取)下一级的对象:this.nextHandler = _nextHandler;
4、责任链 设计模式 优缺点?
优点:将请求者和处理者分开,两者解耦,提供系统灵活性。
缺点:请求都是从链头遍历到链尾,当链很长的时候,性能是个很大的问题。
二、责任链模式(十二)
转自或参考:责任链模式(十二)
https://blog.csdn.net/afei__/article/details/80677711
一、引子
古代女子有“三从四德”之说,“三从”即指“未嫁从父、既嫁从夫、夫死从子”。也就是说,一位女性在结婚之前要听从于父亲,结婚之后要听从于丈夫,如果丈夫死了还要听从于儿子。举例来说,如果一位女性要出去逛街,在她出嫁前必须征得父亲的同意,出嫁之后必须获得丈夫的许可,那丈夫死了怎么办?那就得问问儿子是否允许自己出去逛街。估计你接下来马上要问:“要是没有儿子怎么办?”那就请示小叔子、侄子等。在父系社会中,妇女只占从属地位,现在想想中国古代的妇女还是挺悲惨的,连逛街都要多番请示。作为父亲、丈夫或儿子,只有两种选择:要不承担起责任来,允许她或不允许她逛街;要不就让她请示下一个人,这是整个社会体系的约束,应用到我们项目中就是业务规则。
1. 针对这个例子,我们可以先定义一个女子的接口,如下:
public interface IWoman {
int getType();
// 获得个人请示, 你要干什么? 出去逛街? 约会?还是看电影?
String getRequest();
}
2. 然后定义一个古代女子的类,实现这个接口
public class Woman implements IWoman {
// 通过一个int类型的参数来描述妇女的个人状况 1--未出嫁 2--出嫁 3--夫死
private int type = 0;
// 女子的请示
private String request = "";
// 构造函数传递过来请求
public Woman(int _type, String _request) {
this.type = _type;
this.request = _request;
}
// 获得自己的状况
public int getType() {
return this.type;
}
// 获得妇女的请求
public String getRequest() {
return this.request;
}
}
二、错误示范
假如你之前并不知道责任链模式,你可能会如下设计:
1. 父亲、丈夫、儿子都是处理者,可以这么定义
public interface IHandler {
// 你要处理这个请求
void HandleMessage(IWoman woman);
}
public class Father implements IHandler {
// 未出嫁的女儿请示父亲
public void HandleMessage(IWoman woman) {
System.out.println("父亲的答复是:同意");
}
}
public class Husband implements IHandler {
// 妻子请示丈夫
public void HandleMessage(IWoman woman) {
System.out.println("丈夫的答复是: 同意");
}
}
public class Son implements IHandler {
// 母亲请示儿子
public void HandleMessage(IWoman woman) {
System.out.println("儿子的答复是: 同意");
}
}
2. 如上,还差一个场景类来模拟这个场景了
class Client {
public static void main(String[] args) {
// 随机挑选几个女性
Random rand = new Random();
ArrayList<IWoman> arrayList = new ArrayList();
for (int i = 0; i < 5; i++) {
arrayList.add(new Woman(rand.nextInt(4), "我要出去逛街"));
}
// 定义三个请示对象
IHandler father = new Father();
IHandler husband = new Husband();
IHandler son = new Son();
for (IWoman woman : arrayList) {
if (woman.getType() == 1) { // 未结婚少女, 请示父亲
System.out.println("
--------女儿向父亲请示-------");
father.HandleMessage(woman);
} else if (woman.getType() == 2) { // 已婚女子, 请示丈夫
System.out.println("
--------妻子向丈夫请示-------");
husband.HandleMessage(woman);
} else if (woman.getType() == 3) { // 母亲请示儿子
System.out.println("
--------母亲向儿子请示-------");
son.HandleMessage(woman);
} else {
// 暂时什么也不做
}
}
}
}
3. 这样也可以正常运行,但是会存在以下几个问题:
- 职责界定不清晰
对女儿提出的请示,应该在父亲类中做出决定,因此Father
类应该是知道女儿的请求自己处理,而不是在Client
类中进行组装,不应该是这样的。 - 代码臃肿
我们在Client
类中写了很多if...else
的判断条件,而且能随着能处理该类型的请示人员越多,if...else
的判断就越多,代码就越来越臃肿,可读性就越来越低。 - 耦合严重
这是什么意思呢,我们要根据Woman
的type
来决定使用IHandler
的那个实现类来处理请求。有一个问题是:如果IHandler
的实现类继续扩展怎么办?修改Client
类?这与开闭原则违背。 - 异常情况欠考虑
女子提出一个请示,必然要获得一个答复,甭管是同意还是不同意,总之是要一个答复的,而且这个答复是唯一的,不能说是父亲作出一个决断,而丈夫也作出了一个决断,正确的顺序应该是请求一层一层的传递的,父亲处理过后再由丈夫处理。
三、正确示范
虽然你还不知道责任链模式是怎样定义的,但是哦我们可以先来看一下它可以怎样优雅地处理上面这种情况。
1. 首先重新定义一个抽象的 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(IWoman woman) {
if (woman.getType() == this.level) {
this.response(woman);
} else {
if (this.nextHandler != null) { // 有后续环节,
this.nextHandler.HandleMessage(woman); // 才把请求往后递送this.nextHandler.HandleMessage(women);
} else { // 已经没有后续处理人了, 不用处理了
System.out.println("---没地方请示了, 按不同意处理---
");
}
}
}
// 如果不属于你处理的请求, 你应该让她找下一个环节的人, 如女儿出嫁了, 还向父亲请示是否可以逛街, 那父亲就应该告诉女儿, 应该找丈夫请示
public void setNext(Handler _nextHandler) {
this.nextHandler = _nextHandler;
}
// 有请示那当然要回应
protected abstract void response(IWoman woman);
}
2. 定义具体的实现类
public class Father extends Handler {
// 父亲只处理女儿的请求
public Father() {
super(Handler.FATHER_LEVEL_REQUEST);
}
protected void response(IWoman woman) {
System.out.println("父亲的答复是:同意
");
}
}
public class Husband extends Handler {
// 丈夫只处理妻子的请求
public Husband() {
super(Handler.HUSBAND_LEVEL_REQUEST);
}
protected void response(IWoman woman) {
System.out.println("丈夫的答复是: 同意
");
}
}
public class Son extends Handler {
// 儿子只处理母亲的请求
public Son() {
super(Handler.SON_LEVEL_REQUEST);
}
protected void response(IWoman woman) {
System.out.println("儿子的答复是: 同意
");
}
}
3. 模拟一个场景
class Client {
public static void main(String[] args) {
// 随机挑选几个女性
Random rand = new Random();
ArrayList<IWoman> arrayList = new ArrayList();
for (int i = 0; i < 5; i++) {
arrayList.add(new Woman(rand.nextInt(4), "我要出去逛街"));
}
// 定义三个请示对象
Handler father = new Father();
Handler husband = new Husband();
Handler son = new Son();
// 设置请示顺序
father.setNext(husband);
husband.setNext(son);
for (IWoman woman : arrayList) {
father.HandleMessage(woman);
}
}
}
4. 分析
结果也正确,业务调用类 Client
也不用去做判断到底是需要谁去处理,而且 Handler
抽象类的子类可以继续增加下去,只需要扩展传递链而已,调用类可以不用了解变化过程,甚至是谁在处理这个请求都不用知道。在这种模式下,该父亲处理就父亲处理,不该父亲处理就往下传递。 这就是责任链模式。
四、定义
使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
五、类图及通用模版
一种通用模版的写法如下:
1. 抽象处理者
public abstract class Handler {
private Handler nextHandler;
// 每个处理者都必须对请求做出处理
public final Response handleMessage(Request request) {
Response response = null;
// 判断是否是自己的处理级别
if (this.getHandlerLevel().equals(request.getRequestLevel())) {
response = this.echo(request);
} else { // 不属于自己的处理级别
// 判断是否有下一个处理者
if (this.nextHandler != null) {
response = this.nextHandler.handleMessage(request);
} else {
// 没有适当的处理者, 业务自行处理
}
}
return response;
}
// 设置下一个处理者是谁
public void setNext(Handler _handler) {
this.nextHandler = _handler;
}
// 每个处理者都有一个处理级别
protected abstract Level getHandlerLevel();
// 每个处理者都必须实现处理任务
protected abstract Response echo(Request request);
}
这里抽象的处理者实现三个职责:一是定义一个请求的处理方法 handleMessage
,也是唯一对外开放的方法;二是定义一个链的编排方法 setNext
,设置下一个处理者; 三是定义了具体的请求者必须实现的两个方法:定义自己能够处理的级别 getHandlerLevel
和具体的处理任务 echo
。
注意这里 handleMessage
方法前面的 final
关键字,这里其实就是模版方法的一个使用,父类定义了部分方法的执行顺序,实际实现却是子类完成。
2. 其它相关接口类
public interface Request {
// 获取请求的等级
Level getRequestLevel();
}
public interface Response {
// 返回的结果
}
public interface Level {
// 等级的定义
}
3. 场景类
class Client {
public static void main(String[] args) {
// 声明所有的处理节点
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
// 设置链中的阶段顺序1-->2-->3
handler1.setNext(handler2);
handler2.setNext(handler3);
Request request = new ConcreteRequest();
// 提交请求, 返回结果
Response response = handler1.handleMessage(request);
}
}
其实在实际应用中,一般还有有一个类来封装责任链,完成责任链的顺序编排,并返回链中的第一个处理者,这样上层逻辑就不再需要设置具体的责任链了,简化了上层逻辑,减少了模块间的耦合,系统也会更灵活。
六、优缺点
- 优点
将请求者和处理者分开,两者解耦,提供系统灵活性。 - 缺点
请求都是从链头遍历到链尾,当链很长的时候,性能是个很大的问题。
一般我们可以在Handler
中设置一个最大节点数量,在setNext
方法中判断是否已经超过最大节点数,超过则不允许继续添加处理者,避免无意识的破坏系统性能。