当我们想要让一个以上的对象有机会能够处理某个请求时,就是用责任链模式,每个对象依据程序检查请求并对其进行处理,或者将这个传递给下一个对象,链中的每个对象都扮演着处理器的角色,并且都有一个后继对象,如果它可以处理请求,那就处理这个请求,否则直接转移给后继类进行处理。主要用于在窗口系统中,处理类似于鼠标,键盘等的事件类型,优势在于:
1.将请求的发送者和接收者进行解耦;
2科技简化对象,因为不需要关注链的结构;
3通过改变链内的成员或者调动他们的次序,允许动态的新增或者删除责任。
但是缺点也是明显的:
1.它并不能保证能够保证请求一定会执行,如果没有任何对象处理它,可能会落到链的尾端之外。
2。可能不容易观察运行时的特征,对于除错有一定的障碍。
然我们看一下它的运作图:
结合下面的实例来分析一下责任链模式:
在商场或者是,销售类的工作中,打折是比较常见的一种促销方式,然而,我们假定一个公司中不同等级的人拥有不同的打折权限,所以呢,如果出现一下较高比例打折情况的时候则会朝向他们的上级领导汇报,假定是一级级的汇报,这就很类似于责任链模式的工作方式,对此,我们通过代码来简单的演示一下这种设计模式:
首先呢,我们假定商品已经制作好了,需要设计Handler类,在这里为了实现successor方便将Handler类设定为抽象类:
/*
* 价格处理人,负责处理客户折扣申请
*/
public abstract class PriceHandler {
/*
* 直接后继,用于传递请求
*/
protected PriceHandler successor;
public void setSuccessor(PriceHandler successor) {
this.successor = successor;
}
/*
* 处理折扣申请
*/
public abstract void processDiscount(float discount);
}
之后设计Handler类的继承类,也就是其对于不同要求的实现类:
/*
* 销售,可以批准5%的折扣
*/
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%以内的折扣
*/
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%以内的折扣
*/
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%以内的折扣
*/
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%以内的折扣
* 折扣超出55%, 就拒绝申请
*/
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);
}
}
}
此时,Handler类的实现类暂且设计这几个类,我们需要实例化各个对象,以便于在责任链进行的过程中能够正常工作,所以呢,按照职责单一的原则,我们不能再把类实例化的过程以及责任链传递的过程设计在类Handler中,所以可以参照简单工厂设计模式来进行设计,(不理解见到工厂模式的可以参考个人博客中关于工厂模式的介绍):
public class PriceHandlerFactory {
/*
* 创建PriceHandler的工厂方法
*/
public static PriceHandler createPriceHandler() {
PriceHandler sales = new Sales();
PriceHandler lead = new Lead();
PriceHandler man = new Manager();
PriceHandler dir = new Director();
PriceHandler vp = new VicePresident();
PriceHandler ceo = new CEO();
sales.setSuccessor(lead);
lead.setSuccessor(man);
man.setSuccessor(dir);
dir.setSuccessor(vp);
vp.setSuccessor(ceo);
return sales;
}
}
这个类中定义类一个静态的实例化各个对象的方法,可以直接实例化各个对象,同时,将责任链的传递的过程封锁在这个方法之中。实现了各个实现类(处理器)之间的连接,也就产生了责任链模式的雏形。
定义一个顾客类,假设这个顾客购买了产品,通过使用Random类来产生随机的销售折扣额度:
import java.util.Random;
import com.imooc.pattern.cor.handler.PriceHandler;
import com.imooc.pattern.cor.handler.PriceHandlerFactory;
/*
* 客户申请折扣
*/
public class Customer {
public void setPriceHandler(PriceHandler priceHandler) {
this.priceHandler = priceHandler;
}
private PriceHandler priceHandler;
public void requestDiscount(float discount){
priceHandler.processDiscount(discount);
}
public static void main(String[] args){
Customer customer = new Customer();
customer.setPriceHandler(PriceHandlerFactory.createPriceHandler());
Random rand = new Random();
for(int i=1;i<=100;i++){
System.out.print(i+":");
customer.requestDiscount(rand.nextFloat());
}
}
}
然而,当我们需要做出改变的时候,例如这时候我们需要添加一个小组长,这个小组长来带领进行销售,所以我们需要做出改变,根据设计的开闭原则,我们大部分情况下需要新加一个类,而不是过多的去修改原来的类,所以这时候需要添加一个leader类:
/*
* 销售小组长, 可以批准15%以内的折扣
*/
public class Lead extends PriceHandler {
@Override
public void processDiscount(float discount) {
if(discount<=0.15){
System.out.format("%s批准了折扣:%.2f%n",this.getClass().getName(),discount);
}else{
successor.processDiscount(discount);
}
}
}
然而,我们不需要对客户端做出改变,这就体现了解耦的好处,运行结果如下: