责任链模式(Chain of responsibility pattern):通过责任链模式,你可以为某个请求创建一个对象链.每个对象依序检查此请求并对其进行处理或者将它传给链中的下一个对象.
先说一个古代很有意思的游戏:击鼓传花
击鼓传花在古时候是一种很热闹和紧张的饮酒赋诗游戏,在酒宴上游戏参与者依次坐定自己的位置,由另外一个人击鼓,为了以示公平,击鼓和传花的位置都是公开的。击鼓时,花就开始依次传递,鼓声一停,花落在那个人手上,那个人就要出来饮酒赋诗,表演一下。
比如图中的例子,姑娘一、二、三和公子一、二、三都是游戏参与者,他们组成了一个环链,当鼓声开始的时候,花就在姑娘一那里开始传递,姑娘一传给姑娘二,姑娘二传给姑娘三,姑娘三传给公子一,公子一传给公子二,公子二传给公子三,公子三又传回给姑娘一,如此循环,直到鼓声停止,花落在谁的手上,谁就是中奖者,就要出来饮酒赋诗。
击鼓传花便是责任链模式的应用,责任链可能是一条直线、一个环链或是一个树状的一部分。
责任链模式的结构:
将接受者对象连成一条链,并在该链上传递请求,知道有一个接受者对象处理它。通过让更多对象有机会处理请求,避免了请求发送者和接受者之间的耦合。
责任链模式涉及的角色如下:
抽象处理者(Handler)角色:定义出一个处理请求的接口,如果需要,接口可以定义出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。
具体处理者(ConcreteHandler)角色:具体处理者接收到请求之后,可以选择将请求处理掉,或者将请求传递给下家,有具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
接下来我们用一个很形象的例子来理解责任链模式:(有求必应的销售团队)
现在买房非常兴起,不同的房子有不同的折扣,当然有些折扣是最基层的销售人员都可以处理的,但是随着折扣越来越大,所能处理的人员的等级也越来越高,就比如上面的比例图来说。如果折扣不超过5%的,那么销售人员是可以处理的,但是超过5%就由上级来处理;同样的当折扣超过30%时销售经理就不能处理了,就得由更高级的来处理,以此类推一直到CEO。
那么接下来我们来看一下代码的实现:
价格处理人:
/**
* 价格处理人,负责处理客户的折扣申请
* @author JUN
*
*/
public abstract class PriceHandler {
//直接后继,用于传递申请
protected PriceHandler successor;
public void setSuccessor(PriceHandler successor) {
this.successor = successor;
}
//处理折扣申请
public abstract void processDiscount(float discount);
//创建PriceHandler的工厂方法
public static PriceHandler creatPriceHandler() {
return null;
}
}
首先,销售先接手:
/**
* 销售,可以批准5%以内的折扣
* @author JUN
*
*/
public class Sales extends PriceHandler {
@Override
public void processDiscount(float discount) {
if(discount<=0.05){
System.out.format("%s批准了折扣:%.2f%n", this.getClass().getName(),discount);
}else{
successor.processDiscount(discount);
}
}
}
超过了就换销售经理来处理:
/**
* 销售经理,可以批准30%以内的折扣
* @author JUN
*
*/
public class Manager extends PriceHandler {
@Override
public void processDiscount(float discount) {
if(discount<=0.3){
System.out.format("%s批准了折扣:%.2f%n", this.getClass().getName(),discount);
}else{
successor.processDiscount(discount);
}
}
}
还超过就找销售总监:
/**
* 销售总监,可以批准40%以内的折扣
* @author JUN
*
*/
public class Director extends PriceHandler {
@Override
public void processDiscount(float discount) {
if(discount<=0.4){
System.out.format("%s批准了折扣:%.2f%n", this.getClass().getName(),discount);
}else{
successor.processDiscount(discount);
}
}
}
销售总监也没办法,就请动副总:
/**
* 副总,可以批准50%以内的折扣
* @author JUN
*
*/
public class VicePresident extends PriceHandler {
@Override
public void processDiscount(float discount) {
if(discount<=0.5){
System.out.format("%s批准了折扣:%.2f%n", this.getClass().getName(),discount);
}else{
successor.processDiscount(discount);
}
}
}
最后只能请CEO出山了:
/**
* CEO,可以批准55%以内的折扣
* @author JUN
*
*/
public class CEO extends PriceHandler {
@Override
public void processDiscount(float discount) {
if(discount<=0.55){
System.out.format("%s批准了折扣:%.2f%n", this.getClass().getName(),discount);
}else{
System.out.format("%s拒绝了折扣:%.2f%n", this.getClass().getName(),discount);
}
}
}
好了,接下来在价格处理人(PriceHandler)中添加传递申请折扣的处理:
最后,我们创建一个千姿百态的消费者来测试一下:
/**
* 客户,请求折扣
* @author JUN
*
*/
public class Customer {
protected PriceHandler priceHandler;
public void setPriceHandler(PriceHandler priceHandler) {
this.priceHandler = priceHandler;
}
public void requestDiscount(float discount) {
priceHandler.processDiscount(discount);
}
public static void main(String[] args) {
Customer customer = new Customer();
customer.setPriceHandler(PriceHandler.creatPriceHandler());
Random ran = new Random();
for(int i= 0;i<=30;i++){
System.out.print(i+":");
customer.requestDiscount(ran.nextFloat());
}
}
}
运行结果查看,所有结果都有求必应:
纯的与不纯的责任链模式
一个纯的责任链模式要求一个具体的处理者对象只能在两个行为中选择一个:一是承担责任,而是把责任推给下家。不允许出现某一个具体处理者对象在承担了一部分责任后又 把责任向下传的情况。
在一个纯的责任链模式里面,一个请求必须被某一个处理者对象所接收;在一个不纯的责任链模式里面,一个请求可以最终不被任何接收端对象所接收。
纯的责任链模式的实际例子很难找到,一般看到的例子均是不纯的责任链模式的实现。有些人认为不纯的责任链根本不是责任链模式,这也许是有道理的。但是在实际的系统里,纯的责任链很难找到。如果坚持责任链不纯便不是责任链模式,那么责任链模式便不会有太大意义了。
责任链模式的优缺点:
优点:
在责任链模式中,作为请求接收者的多个对象通过对其后继的引用而连接起来形成一条链。请求在这条链上传递,知道链上某一个接收者处理这个请求。每个接收者都可以选择自行处理请求或是向后继传递请求。
发出请求的客户端并不知道链上的哪一个接受者会处理这个请求,从而实现了客户端和接收者之间的解耦。
缺点:
1、没有很好的遵循开闭原则
2、执行的性能会变差,编译时会产生出很多的处理对象,但是实际中用到的只是一小部分,而浪费了很大的一部分资源。
3、当请求的处理需要多个对象进行判断时,这时的执行效率会较低。