设计模式之责任链模式
今天我们来学一种开发中经常会遇到的一种模式,比如web开发中的Filter过滤器、Interceptor拦截器等,这些用的就是 责任链模式。
责任链模式算是23中设计模式中常见的模式之一了,下面讲一下我对这个设计模式的一些理解:
先来看看我们不使用责任链模式处理一段字符串的代码:
/**
* 不使用责任链模式的处理逻辑
*/
public class ChainOfResponsibility_01 {
public static void main(String[] args) {
String string = "HELLO";
//第一步添加下划线
string = doSomething1(string);
//第二步添加world
string = doSomething2(string);
//第三步转小写
string = doSomething3(string);
System.out.println(string);
}
private static String doSomething1(String s){
return s+"_";
}
private static String doSomething2(String s){
return s+"world";
}
private static String doSomething3(String s){
return s.toLowerCase();
}
}
按照我们日常的开发,对数据的不同处理方式可能会拆分成几个小的方法对其进行处理,每个方法里面负责独立的逻辑。这样每个步骤有变化,只需要修改对应的方法内容即可。
但是没新增加一个处理逻辑,都要显示调用,那有没有一种方式可以简化这种调用呢。
我们来看看下面这段代码:
public class ChainOfResponsibility_02 {
public static void main(String[] args) {
String string = "HELLO";
ChainFilter chainFilter = new ChainFilter();
chainFilter.add(new ConcatDownLineFilter());
chainFilter.add(new ConcatWorldFilter());
chainFilter.add(new ConvertLowerCaseFilter());
String s = chainFilter.doFilter(string);
System.out.println(s);
}
interface Filter{
abstract String doFilter(String s);
}
static class ConcatDownLineFilter implements Filter{
@Override
public String doFilter(String s) {
return s+"_";
}
}
static class ConcatWorldFilter implements Filter{
@Override
public String doFilter(String s) {
return s+"world";
}
}
static class ConvertLowerCaseFilter implements Filter{
@Override
public String doFilter(String s) {
return s.toLowerCase();
}
}
static class ChainFilter{
List<Filter> filters = new ArrayList<>();
public void add(Filter filter){
this.filters.add(filter);
}
public String doFilter(String string){
for (Filter filter : filters) {
string = filter.doFilter(string);
}
return string;
}
}
}
从以上代码可以看到,这里使用了初步的责任链模式,ChainFilter中保存了所有的filter,当调用ChainFilter的doFilter时,会遍历保存的所有filter,依次进行逻辑处理,最终可以得到我们想要的结果,而main中只需要按照顺序添加Filter即可。
如果这个时候存在两个ChainFilter的时候,我想传入的参数都需要经过这两个ChainFilter,那这个时候怎么办呢:
一种是等第一个ChainFilter处理完成后,再调用第二个ChainFilter进行处理;
另一种就是讲两个ChainFilter合并成一个ChainFilter,然后调用一次doFilter即可。
第一种方式简单,这里我们演示第二种方式,看代码:
public class ChainOfResponsibility_03 {
public static void main(String[] args) {
String string = "HELLO";
//第一个ChainFilter
ChainFilter chainFilter = new ChainFilter()
.add(new ConcatDownLineFilter())
.add(new ConcatWorldFilter());
//将第二个ChainFilter添加到第一个
chainFilter.add(new ChainFilter().add(new ConvertLowerCaseFilter()));
//执行
String s = chainFilter.doFilter(string);
System.out.println(s);
}
interface Filter{
abstract String doFilter(String s);
}
static class ConcatDownLineFilter implements Filter{
@Override
public String doFilter(String s) {
return s+"_";
}
}
static class ConcatWorldFilter implements Filter{
@Override
public String doFilter(String s) {
return s+"world";
}
}
static class ConvertLowerCaseFilter implements Filter{
@Override
public String doFilter(String s) {
return s.toLowerCase();
}
}
//让ChainFilter也实现Filter,这样其中的属性filters就可以将所有实现了Filter的都保存,
//包括其余的ChainFilter
static class ChainFilter implements Filter{
List<Filter> filters = new ArrayList<>();
//这里返回ChainFilter,采用了链式编程的方式,操作更方便
public ChainFilter add(Filter filter){
this.filters.add(filter);
return this;
}
@Override
public String doFilter(String string){
for (Filter filter : filters) {
string = filter.doFilter(string);
}
return string;
}
}
}
再继续,如果有要求:当处理到某一个filter时,如果符合条件,要求可以中断调用,只返回当前已经处理的结果,不再往后执行:
public class ChainOfResponsibility_04 {
public static void main(String[] args) {
String string = "HELLO";
//第一个ChainFilter
ChainFilter chainFilter = new ChainFilter()
.add(new ConcatDownLineFilter())
.add(new ConcatWorldFilter());
//将第二个ChainFilter添加到第一个
chainFilter.add(new ChainFilter().add(new ConvertLowerCaseFilter()));
//执行
chainFilter.doFilter(string);
}
interface Filter{
abstract boolean doFilter(String s);
}
static class ConcatDownLineFilter implements Filter{
@Override
public boolean doFilter(String s) {
if(null==s || "".equals(s)){
return false;
}
if(s.contains("_")){
return false;
}
System.out.println(s+"_");
return true;
}
}
static class ConcatWorldFilter implements Filter{
@Override
public boolean doFilter(String s) {
if(null==s || "".equals(s)){
return false;
}
if(s.startsWith("H")){
return false;
}
System.out.println(s+" world");
return true;
}
}
static class ConvertLowerCaseFilter implements Filter{
@Override
public boolean doFilter(String s) {
if(null==s || "".equals(s)){
return false;
}
System.out.println(s.toLowerCase());
return true;
}
}
//让ChainFilter也实现Filter,这样其中的属性filters就可以将所有实现了Filter的都保存,
//包括其余的ChainFilter
static class ChainFilter implements Filter{
List<Filter> filters = new ArrayList<>();
//这里返回ChainFilter,采用了链式编程的方式,操作更方便
public ChainFilter add(Filter filter){
this.filters.add(filter);
return this;
}
@Override
public boolean doFilter(String string){
for (Filter filter : filters) {
boolean b = filter.doFilter(string);
if(!b){
return false;
}
}
return true;
}
}
}
以上代码,做了部分修改,让doFilter返回boolean值,这样就可以通过返回的值,判断是否需要继续执行其他Filter,而每个Filter里面也可以添加自己的中断逻辑。就类似于web中的拦截器或过滤器,一旦发现权限不足,那么就直接返回,不再往后面进行处理。
现在又有一个要求,模拟处理字符串,要求正序先处理完,然后再逆向处理。(注意:这里的要求并不是当前方法处理完后,立刻处理后续逻辑,而是等所有的Filter都先处理完成后,所有的Filter再从后往前处理):
public class ChainOfResponsibility_05 {
public static void main(String[] args) {
String string = "HELLO";
//第一个ChainFilter
new ChainFilter().add(new ConcatAFilter())
.add(new ConcatBFilter())
.add(new ConcatCFilter())
.doFilter(string);
}
interface Filter{
abstract void doFilter(String s, ChainFilter f);
}
static class ConcatAFilter implements Filter {
@Override
public void doFilter(String s, ChainFilter f) {
System.out.println(s + "_A");
if(f.hasNext()) {
f.nextFilter().doFilter(s, f);
}
System.out.println(s + "_a");
}
}
static class ConcatBFilter implements Filter {
@Override
public void doFilter(String s, ChainFilter f) {
System.out.println(s + "_B");
if(f.hasNext()) {
f.nextFilter().doFilter(s, f);
}
System.out.println(s + "_b");
}
}
static class ConcatCFilter implements Filter {
@Override
public void doFilter(String s, ChainFilter f) {
System.out.println(s + "_C");
if(f.hasNext()) {
f.nextFilter().doFilter(s, f);
}
System.out.println(s + "_c");
}
}
static class ChainFilter{
List<Filter> filters = new ArrayList<>();
int index = -1;//记录索引下标
int count = 0;//filter的总数
//这里返回ChainFilter,采用了链式编程的方式,操作更方便
public ChainFilter add(Filter filter){
this.filters.add(filter);
count++;
return this;
}
public void doFilter(String s){
if(hasNext()) {
this.nextFilter().doFilter(s, this);
}
}
public boolean hasNext(){
if(index+1>=count){
return false;
}
index++;
return true;
}
public Filter nextFilter(){
return this.filters.get(index);
}
}
}
输出结果:
HELLO_A
HELLO_B
HELLO_C
HELLO_c
HELLO_b
HELLO_a
以上这段代码,做了比较大的改动,首先是Filter的接口中,新增一个ChainFilter,其次ChainFilter不再实现Filter(虽然也可以实现Filter),新增了两个接口,一个是hasNext(),一个nextFilter,其目的是为了判断ChainFilter中索引下标是否超出了filters的总数,如果没有,那么可以继续获取下一个Filter,并调用其doFilter操作,否则执行doFilter后的代码。
以上这段代码类似于Servlet中Filter和FilterChain的定义,我们来看看Servlet中是怎么定义的:
public interface Filter {
void init(FilterConfig var1) throws ServletException;
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
void destroy();
}
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
以上是Servlet中的责任链的主要两个接口。
还有一种责任链的方式,是真正的逻辑上的链条方式,就是ChainFilter在添加Filter时,记录每个Filter的上一个Filter和下一个Filter,形成真正的链条。但是这种方式类似与集合中的LinkedList,遍历起来耗费性能,没有数组形式的效率高。
最后,是关于责任链的官方定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。