23种设计模式-行为型模式-责任链模式

1. 简介

责任链模式:如果有多个对象都有机会处理请求,责任链可使请求的发送者和接受者解耦,请求延着责任链传递,直到有一个对象处理了它为止
注意:责任链模式也叫职责链模式。

2. 结构

职责链模式主要包含以下角色:

  1. 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  2. 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  3. 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
    在这里插入图片描述
    在这里插入图片描述

3. 具体事例

员工申请采购请求,由组长、部长、副总、总裁其中的一位批准即通过,但组长、部长、副总、总裁所能批准的金额有一定限度:
组长:[0,5000)
部长:[5000,10000)
副总:[10000,50000)
总裁:[50000,+)

//批准者
public abstract class Approver {
	Approver successor;
	String name;
	public Approver(String name) {
		this.name = name;
	}
	public void setSuccessor(Approver successor) {
		this.successor = successor;
	}
	public abstract void ProcessRequest(PurchaseRequest req);
}

//组长
public class GroupApprover extends Approver {
	public GroupApprover(String name) {
		super(name+" GroupLeader");
	}
	@Override
	public void ProcessRequest(PurchaseRequest req) {
		if(req.getSum()<5000) {
			System.out.println("**这个请求 "+req.getId()+"将被 "+name+" 处理");
		}else {
			if(successor!=null) {
				successor.ProcessRequest(req);
			}
		}
	}
}

//部长
public class DepartmentApprover extends Approver {
	public DepartmentApprover(String name) {
		super(name+" DepartmentLeader");
	}
	@Override
	public void ProcessRequest(PurchaseRequest req) {
		if(req.getSum()>=5000&&req.getSum()<10000) {
			System.out.println("**这个请求 "+req.getId()+"将被 "+name+" 处理");
		}else {
			if(successor!=null) {
				successor.ProcessRequest(req);
			}
		}
	}
}

//副总
public class VicePresidentApprover extends Approver {
	public VicePresidentApprover(String name) {
		super(name+" VicePresidentLeader");
	}
	@Override
	public void ProcessRequest(PurchaseRequest req) {
		if(req.getSum()>=10000&&req.getSum()<50000) {
			System.out.println("**这个请求 "+req.getId()+"将被 "+name+" 处理");
		}else {
			if(successor!=null) {
				successor.ProcessRequest(req);
			}
		}
	}
}

//老总
public class PresidentApprover extends Approver {
	public PresidentApprover(String name) {
		super(name+" PresidentLeader");
	}
	@Override
	public void ProcessRequest(PurchaseRequest req) {
		if(req.getSum()>=50000) {
			System.out.println("**这个请求 "+req.getId()+"将被 "+name+" 处理");
		}else {
			if(successor!=null) {
				successor.ProcessRequest(req);
			}
		}
	}
}

//采购请求
public class PurchaseRequest {
	private int type;
	private int number;
	private float price;
	private int id;
	public PurchaseRequest(int type, int number, float price) {
		this.type = type;
		this.number = number;
		this.price = price;
	}
	public int getType() {
		return type;
	}
	public float getSum() {
		return number*price;
	}
	public int getId() {
		return (int)(Math.random()*1000);
	}
}

//员工
public class Client {
	//发送请求
	public PurchaseRequest sendRequest(int type,int number,float price) {
		return new PurchaseRequest(type, number, price);
	}
}

//测试
public class MainTest {
	public static void main(String[] args) {
		Client mClient=new Client();
		Approver GroupLeader=new GroupApprover("Tom");
		Approver DepartmentLeader=new DepartmentApprover("Jerry");
		Approver VicePresident=new VicePresidentApprover("Kate");
		Approver President=new PresidentApprover("Bush");
		
		GroupLeader.setSuccessor(VicePresident);
		DepartmentLeader.setSuccessor(President);
		VicePresident.setSuccessor(DepartmentLeader);
		President.setSuccessor(GroupLeader);//老总那不满足后又交给组长
		
		GroupLeader.ProcessRequest(mClient.sendRequest(1, 100, 40));
		VicePresident.ProcessRequest(mClient.sendRequest(2, 200, 40));
		VicePresident.ProcessRequest(mClient.sendRequest(3, 300, 40));
		VicePresident.ProcessRequest(mClient.sendRequest(4, 400, 140));
	}
}

4. 优缺点

优点:

  • 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
  • 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
  • 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
  • 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
  • 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

缺点:

  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

5. 适用场景

适用场景:

  • 有多个对象可以处理一个请求
  • 不明确接受者的情况
  • 有序、无序链、线性、树形、环形链

6. 模式的扩展

职责链模式存在以下两种情况。

  1. 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
  2. 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。

7. 代码中的运用

  • servlet中的filter
  • dubbo中的filter
  • mybatis中的plugin 这3个框架在实现责任链方式不尽相同。

7.1 servlet中的Filter,servlet中分别定义了一个 Filter和FilterChain的接口,核心代码如下:

public final class ApplicationFilterChain implements FilterChain {
    private int pos = 0; //当前执行filter的offset
    private int n; //当前filter的数量
    private ApplicationFilterConfig[] filters;  //filter配置类,通过getFilter()方法获取Filter
    private Servlet servlet
  
    @Override
    public void doFilter(ServletRequest request, ServletResponse response) {
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = filterConfig.getFilter();
            filter.doFilter(request, response, this);
        } else {
            // filter都处理完毕后,执行servlet
            servlet.service(request, response);
        }
    }
  
}
//解决全站中文乱码
public class Myfilter1 implements Filter{

    @Override
    public void destroy() {      
    }
    @Override
    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {
        //进行Request和Response对象的类型强转
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        //解决中文乱码问题1.请求的中文乱码Get 和 Post 2.响应中文乱码
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        //3.创建自定义的Request
        
        //4.放行(不一定放行)
        chain.doFilter(request, response);

		//5.放行后的操作(不一定有)
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {  
    }

}

首先由ApplicationFilterChain 调用Filter,然后如果Filter放行的话Filter又会去调用ApplicationFilterChain,如此反复直至ApplicationFilterChain的doFilter方法进入else

7.2 Dubbo中的Filter:

Dubbo在创建Filter的时候是另外一个方法,通过把Filter封装成 Invoker的匿名类,通过链表这样的数据结构来完成责任链,核心代码如下:

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker;
    //只获取满足条件的Filter
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
    if (filters.size() > 0) {
        for (int i = filters.size() - 1; i >= 0; i --) {
            final Filter filter = filters.get(i);
            final Invoker<T> next = last;
            last = new Invoker<T>() {
                ...
                public Result invoke(Invocation invocation) throws RpcException {
                    return filter.invoke(next, invocation);
                }
                ...
            };
        }
    }
    return last;
}

Dubbo的责任链就没有类似FilterChain这样的类吧Filter和调用Invoker结合起来,而是通过创建一个链表,调用的时候我们只知道第一个节点,每个节点包含了下一个调用的节点信息。 这里的虽然Invoker封装Filter没有显示的指定next,但是通过java匿名类和final的机制达到同样的效果。
在这里插入图片描述

7.3 Mybatis中的Plugin:

Mybatis可以配置各种Plugin,无论是官方提供的还是自己定义的,Plugin和Filter类似,就在执行Sql语句的时候做一些操作。Mybatis的责任链则是通过动态代理的方式,使用Plugin代理实际的Executor类。(这里实际还使用了组合模式,因为Plugin可以嵌套代理),核心代码如下:

public class Plugin implements InvocationHandler{
    private Object target;
    private Interceptor interceptor;
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {      
        if (满足代理条件) {
            return interceptor.intercept(new Invocation(target, method, args));
        }
        return method.invoke(target, args);     
    }
   
    //对传入的对象进行代理,可能是实际的Executor类,也可能是Plugin代理类
    public static Object wrap(Object target, Interceptor interceptor) {
  
        Class<?> type = target.getClass();
        Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
        if (interfaces.length > 0) {
            return Proxy.newProxyInstance(
                    type.getClassLoader(),
                    interfaces,
                    new Plugin(target, interceptor, signatureMap));
        }
        return target;
    }
} 

简单的示意图如下:
在这里插入图片描述
servlet、Dubbo、Mybatis对责任链模式的不同实现手段,其中Servlet是相对比较清晰,又易于实现的方式,而Dubbo和Mybatis则适合在原有代码基础上,增加责任链模式代码改动量最小的。

猜你喜欢

转载自blog.csdn.net/qq_22771739/article/details/88856262