所谓设计模式就是把简单的问题复杂化,哪部分的代码会变化就封装哪部分
我们来看一个场景:我们的网站上会提供一些留言面板,用户可以自由输入留言,所以我们需要对这些留言进行限制,防止黑客攻击和一些不良言论。
按照传统的方式,代码如下:
public static void main(String[] args) { Msg msg = new Msg(); msg.setMsg("大家好:!!!<script> </script>,欢迎大家访问CodeSavant.com,希望大家能摆脱996"); String s = msg.getMsg(); s = s.replace("<", "").replace(">", ""); msg.setMsg(s); s = s.replaceAll("996","965"); System.out.println(s); }
这显然不适合扩展,我们在替换字符串的这个处理过程是一步接一步的,于是就可以使用到责任链设计模式
责任链设计模式必备的要素:
- 抽象处理者
- 具体处理者
- 请求
首先,我们来定义一个抽象处理者
public interface Filter { void doFilter(Msg msg); }
接着我们定义具体的处理者来处理
public class BadFilter implements Filter{ @Override public void doFilter(Msg msg) { String s = msg.getMsg(); s = s.replaceAll("996","965"); msg.setMsg(s); } } public class CodeFilter implements Filter{ @Override public void doFilter(Msg msg) { String s = msg.getMsg(); s = s.replace("<", "").replace(">", ""); msg.setMsg(s); } }
最后我们在客户端代码中调用Filter进行处理
public static void main(String[] args) { Msg msg = new Msg(); msg.setMsg("大家好:!!!<script> </script>,欢迎大家访问CodeSavant.com,希望大家能摆脱996"); new BadFilter().doFilter(msg); new CodeFilter().doFilter(msg); System.out.println(msg); }
执行之后可以看到,与我们直接写的代码执行结果一致。但是这还不够完美,因为我们需要一行一行的去调用,这显然不合理。因此我们要进行优化
我们引入一个新的类,即调用链。通过调用链我们可以将每个处理者添加到其中,这样可以实现顺序执行
public class FilterChain { List<Filter> filterList = new ArrayList<>(); public FilterChain addFilter(Filter filter){ filterList.add(filter); return this; } public void doFilter(Msg msg){ for (Filter f : filterList) { f.doFilter(msg); } } }
可以看到,调用链持有处理者的列表,也提供了一个doFiilter方法来执行操作。并且提供了一个addFilter来进行添加处理者,这里可以看到我们每次添加完会将当前的对象返回回去,这个会在建造者模式中谈到。
我们看一下修改完了之后的客户端代码
public static void main(String[] args) { Msg msg = new Msg(); msg.setMsg("大家好:!!!<script> </script>,欢迎大家访问CodeSavant.com,希望大家能摆脱996"); FilterChain filterChain = new FilterChain(); filterChain.addFilter(new BadFilter()).addFilter(new CodeFilter()); filterChain.doFilter(msg); System.out.println(msg); }
很明显方便了很多。
但是仍然有问题,假设现在又有一个新的调用链,我们没法将两个调用链连接起来。于是有以下的解决方案:
我们让FilterChain本身也去实现Filter接口,即FilterChain本身可以看做一个Filter,接下来在调用的时候只需要把他本身当做Filter合并在一起就行。我们来看代码:
public class FilterChain implements Filter{...} public static void main(String[] args) { Msg msg = new Msg(); msg.setMsg("大家好:!!!<script> </script>,欢迎大家访问CodeSavant.com,希望大家能摆脱996"); FilterChain f1 = new FilterChain(); f1.addFilter(new BadFilter()).addFilter(new CodeFilter()); FilterChain f2 = new FilterChain(); f2.addFilter(new HappyFilter()).addFilter(new ByeFilter()); f1.addFilter(f2); f1.doFilter(msg); System.out.println(msg); }
可以看到,在以上的例子中,我们将f2
本身作为一个Filter
全部添加进了f1
中
那么此时我们可以知道:f1
中包含了3个Filter
的对象:BadFilter``CodeFilter``f2
而f2
其本身又包含了两个Filter对象HappyFilter``ByeFilter
这样我们在调用f1.doFilter(msg)
的时候,会按照遍历数组的顺序,首先执行f1
中BadFilter``CodeFilter
的doFilter方法,随后在调用到 f2
的时候,由于f2
其本身也是一个FilterChain,他也是含有自己的数组引用的,于是会调用他的doFilter方法,这样就会随着调用HappyFilter``ByeFilter
的doFilter方法了,有点类似递归的一个结构。
以上就是一个基本的实现。但是如果我们现在又会有新的要求:处理到某个Filter节点的时候就终止调用。
于是我们需要在Filter接口的doFilter方法提供一个返回值,当他返回为false的时候,我们终止整个链条的调用
我们看一下代码:
public interface Filter { boolean doFilter(Msg msg); } public class FilterChain implements Filter{ public boolean doFilter(Msg msg){ for (Filter f : filterList) { if (!f.doFilter(msg)) return false; } return true; } }
我们将BadFilter
的返回值改为false,这样就只执行这一个就不会接着执行了。
原因就在于if (!f.doFilter(msg)) return false;
在这一行代码里面,我们判断如果执行的返回结果是false,那么就直接return false
终止了循环
同理如果这时候f 引用本身指向的对象也是个 FilterChain,根据我们上面的分析,这个时候f里面也会循环执行doFilter,那么只要他里面有一个方法执行的结果返回了false,那么整个循环就返回了false,同样的会返回到外层,这样就完成了递归调用。
最后我们来讲解一下在Java的Servlet中是如何使用责任链模式Filter的
我们先来了解一下背景,在JavaEE中,我们经常需要处理Request和Response对象,并且我们是先处理Request,然后再返回处理Response的。即Request是正向处理先进先出,Response是反向处理后进先出。
我们先来简单的定义一下Request和Response对象
public class Request { String msg; } public class Response { String msg; }
接下来我们在之前的代码的基础上,修改一下FilterChain的代码
public class FilterChain { List<Filter> filterList = new ArrayList<>(); int index = 0; public FilterChain addFilter(Filter filter){ filterList.add(filter); return this; } public boolean doFilter(Request request,Response response){ //判断是否执行完 if (index == filterList.size()) return false; //如果没执行完,取出当前的filter,接着向下执行 Filter f = filterList.get(index); index++; return f.doFilter(request,response,this); } }
这里可以看到,我们添加了一个index属性,用来控制FilterChain的调用顺序。并且在doFilter方法里面,必须要传入两个参数。每次调用完一个Filter之后,将索引的值向后移动一位。并且我们也无需再实现Filter接口了,这也是为了精简代码。
接下来看一下Filter
public interface Filter { boolean doFilter(Request request,Response response,FilterChain filterChain); }
基本没有什么变化,最大的区别就是我们持有了FilterChain,这是为了在具体的实现类中进行顺序调用
我们来看一下具体的实现类
public class CodeFilter implements Filter { @Override public boolean doFilter(Request request,Response response,FilterChain filterChain) { request.msg = request.msg.replace("<","").replace(">",""); filterChain.doFilter(request,response); response.msg += "----调用了CodeFilter"; return true; } } public class BadFilter implements Filter { @Override public boolean doFilter(Request request,Response response,FilterChain filterChain) { request.msg = request.msg.replace("996","965"); filterChain.doFilter(request,response); response.msg += "----调用了BadFilter"; return true; } }
我们可以看到,在具体的实现类中,我们先是处理了Request,然后接着顺序调用FilterChain的doFilter方法,最后才处理Response
我们来看一下客户端代码,并分析一下整个的执行过程
public static void main(String[] args) { Request request = new Request(); request.msg = "大家好:!!!<script> </script>,欢迎大家访问CodeSavant.com,希望大家能摆脱996"; Response response = new Response(); response.msg = ""; FilterChain filterChain = new FilterChain(); filterChain.addFilter(new CodeFilter()).addFilter(new BadFilter()); //.addFilter(new ByeFilter()).addFilter(new HappyFilter()) filterChain.doFilter(request,response); System.out.println(request); System.out.println(response); }
我们添加了CodeFilter和BadFilter
那么此时FilterChain的filterList中包含的两个对象就是CodeFilter、BadFilter
首先我们调用了filterChain.doFilter(request,response);
这行代码
这时候index才刚刚初始化,所以他的值显然为0
所以我们会从filterList中取出CodeFilter的对象,然后再将index++
这时候index的值就已经变成了1
随之调用CodeFilter的doFilter方法
先处理了Request,然后我们可以看到又调用了filterChain.doFilter(request,response);
这里我们如果不接着调用filterChain的话,整个链条就断掉了,因为我们在执行完CodeFilter的方法之后,并不会往后接着走,这主要是因为我们将调用顺序的控制权由FilterChain交给了具体的Filter本身,我们并没有在FilterChain中使用for循环来调用。
接下来划重点:这时我们又进到了FilterChain中,这时候index的值已经是1了,所以会取出BadFilter的对象,接着使index++,那么此时index的值就会变成2了。
紧接着继续调用BadFilter的doFilter方法,先处理Request,然后接着调用filterChain.doFilter(request,response);
但是此时由于index的值已经是2了,这时候filterChain会直接返回,不会接着执行了。随之就会处理BadFilter中的Response。那么此时BadFilter的doFilter也处理完成了。我们知道BadFilter是由CodeFilter先调用了filterChain的doFilter方法,然后再调用到了BadFilter,所以这时候随着BadFilter的执行完毕返回,CodeFilter中filterChain.doFilter(request,response);
这行代码也执行完毕返回了。那么接着就会向下去执行处理CodeFilter中的Response
所以在整个的处理流程中,顺序是这样的:CodeFilter处理Request、CodeFilter调用FilterChain、BadFilter处理Request、BadFilter调用FilterChain执行直接返回、BadFilter处理Response、BadFilter处理完成返回、CodeFilter处理Response、执行完毕。
这样就实现了Request正向,Response反向