本文所有源代码都在我的Github,并且持续更新中…欢迎Star DesignPattern
示例代码
假设我现在有个事件需要上交给政府处理,但是不可能所有小到街坊吵架的事件都交给国家处理,也不可能
大到南水北运的事件交给市级政府处理。所以最先想到用下面的代码来模拟这个情形:
模拟事件类型
public class Event {
public int eventType = -1;
public String eventDetail;
public Event(int eventType,String eventDetail) {
this.eventType = eventType;
this.eventDetail = eventDetail;
}
@Override
public String toString() {
return "Event{" +
"eventType=" + eventType +
", eventDetail='" + eventDetail + '\'' +
'}';
}
}
处理单位接口
public interface Handler {
int COUNTRY_EVENT = 0;
int PROVINCE_EVENT = 1;
int CITY_EVENT = 2;
void process(Event event);
}
市级单位处理类
public class City implements Handler{
@Override
public void process(Event event) {
System.out.println(event);
System.out.println("我来处理这个市级事件");
}
}
省级单位处理类
public class Province implements Handler{
@Override
public void process(Event event){
System.out.println(event);
System.out.println("我来处理这个省份事件");
}
}
国家单位处理类
public class Country implements Handler {
@Override
public void process(Event event) {
System.out.println(event);
System.out.println("我来处理这个国家大事");
}
}
运行类
public class Main {
public static void main(String[] args) {
Event event = new Event(Handler.CITY_EVENT, "街区拆迁");
City city = new City();
Province province = new Province();
Country country = new Country();
switch (event.eventType) {
case Handler.CITY_EVENT:
city.process(event);
break;
case Handler.PROVINCE_EVENT:
province.process(event);
break;
case Handler.COUNTRY_EVENT:
province.process(event);
break;
default:
System.out.println("事件飞往火星处理");
break;
}
}
}
这可能是大多数人最先想到的方法,用switch 或者 if..else来判断,但是如果条件一多比如上面的如果再来个区级事件处理类,村级事件处理类,
过多地if..else或者switch会让代码过于臃肿而可读性差。并且这样的格式也造成耦合过重的情况。并且如果一个省级的事件被转交给了市级单位,
正确的做法应该是由市级单位转交给省级单位处理,而在上面的代码却没表现出来。针对上面的情形,我们可以使用责任链模式来
模拟这一切操作。
责任链模式定义
责任链模式在面向对象程式设计里是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也
知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。直到其中一个对象处理该事件。处理事件后处理停止。该模式还描述了往该处理链的末尾添加新的处理对象的方法。
通用源代码
略
解决方案
我们将Country和Province和City继承Handler,然后把所有的处理类对象用next相连起来,Handler中的handleEvent方法根据事件的类型进行分派给对应的处理对象,
相当于while循环遍历一个链表。具体代码如下:
public abstract class Handler {
public static final int COUNTRY_EVENT = 0;
public static final int PROVINCE_EVENT = 1;
public static final int CITY_EVENT = 2;
public int processType = -1; //能处理的事件的类型
public Handler next = null; //下一个处理类
/**
* 事件分发给合适的处理单位
* @param event
*/
public void handleEvent(Event event) {
if (event.eventType == this.processType) {
process(event);
} else {
if (next == null) {
System.out.println("没有能处理的,自行解决吧");
} else {
next.handleEvent(event);
}
}
}
/**
*
*
* @param handler 注入的next
* @return 注入的next
*/
public Handler setNext(Handler handler){
this.next = handler;
return this.next;
}
abstract void process(Event event);
}
市级单位处理类
public class City extends Handler {
public City(int processType) {
this.processType = this.processType;
}
@Override
public void process(Event event) {
System.out.println(event);
System.out.println("我来处理这个市级事件");
}
}
省级单位处理类
public class Province extends Handler {
public Province(int processType) {
this.processType = processType;
}
@Override
public void process(Event event){
System.out.println(event);
System.out.println("我来处理这个省份事件");
}
}
国家级单位处理类
public class Country extends Handler {
public Country(int processType) {
this.processType = this.processType;
}
@Override
public void process(Event event) {
System.out.println(event);
System.out.println("我来处理这个国家大事");
}
}
Country和City、Province这三个类都继承了Handler,并且实现各自的process方法处理相应等级的事件。
最后是我们的Main类:
public class Main {
public static void main(String[] args) {
Handler city = new City(Handler.CITY_EVENT);
city.setNext(new Province(Handler.PROVINCE_EVENT)).setNext(new Country(Handler.COUNTRY_EVENT));
Event event = new Event(Handler.PROVINCE_EVENT,"城市改名");
city.handleEvent(event);
Event event1 = new Event(10,"火星移民");
city.handleEvent(event1);
}
}
可以看到我们用setNext
设置的访问顺序是City、Province、Country,然后将对于无论什么类型的事件都用City.handleEvent()
来处理
这个事件,因为不是对应能处理的类就会继续往下传递,直到无法处理。而且Handler抽象类的子类可以继续增加下去,只需要扩展传递链而已,调用类可以不用了解变化过程, 甚至是谁在处理这个请求都不用知道。
责任链模式优缺点
优点:
* 将请求和处理分开,请求者可以不用知道具体的处理类。
* 降低耦合度,增加新的处理类很简单。
缺点:
- 每一次处理都要遍历,责任链太长会导致性能受到影响。
- 调试比较麻烦
适用场景
- 想分离一个请求的发送者和接收者
- 在处理对象不多,在运行时根据请求类型动态分派。
- 不想在代码中明确指定处理程序
注意事项
- 链中只有一个对象处理请求
- 有些请求可能无法处理
- 处理链不能过长,影响性能
现有案例
某些限制是针对典型的责任链模式的。在实践中,这些规则是可以改变的,例如,servlet过滤器是一个责任链实现,它允许多个过滤器处理
HTTP请求。
除此之外还有SpringMVC的拦截器以及Tomcat的Pipeline管道处理。他们都允许多个处理对象处理同一个请求。
参考
Follow the Chain of Responsibility
<<设计模式之禅>>