责任链模式
用过tomcat或者springMVC的人应该都对过滤器不陌生吧,过滤器的实现就是责任链模式。
且看如何一步一步推到。话不多说,直接上代码。
//这段代码是实现将骂人词汇过滤:
public class Main {
public static void main(String[] args) {
Msg msg = new Msg();
msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
String r= msg.getMsg();
r = r.replace("shit","ssss");
msg.setMsg(r);
r = r.replace("wtf","what");
msg.setMsg(r);
System.out.println(msg);
}
}
class Msg{
String msg;
@Override
public String toString() {
return "Msg{" +
"msg='" + msg + '\'' +
'}';
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
以上是两个过滤的方法,当有更多的过滤方法的话,如果来一个就往源码中添加一个,那是极其麻烦的,而且不易拓展。
可以这样修改:
将过滤操作抽象成一个接口
interface Filter{
void doFilter(Msg m);
}
定义两个过滤器:
class ShitFilter implements Filter{
@Override
public void doFilter(Msg m) {
String r = m.getMsg();
r = r.replace("shit","ssss");
m.setMsg(r);
}
}
class WtfFilter implements Filter{
@Override
public void doFilter(Msg m) {
String r = m.getMsg();
r = r.replace("wtf","what");
m.setMsg(r);
}
}
现在我们只需要在main中:
public class Main {
public static void main(String[] args) {
Msg msg = new Msg();
msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
new ShitFilter().doFilter(msg);
new WtfFilter().doFilter(msg);
System.out.println(msg);
}
}
这样就实现了过滤的功能,但是此时如果有新的过滤器,还是要添加代码。我们可以将过滤器添加到一个List集合中,这样做是为了之后可以将过滤器定义到配置文件中,而向list中添加元素时,读取配置文件添加,这样就避免了修改源码:
public class Main {
public static void main(String[] args) {
List<Filter> filters = new ArrayList<>();
Msg msg = new Msg();
msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
filters.add(new ShitFilter());
filters.add(new WtfFilter());
for (Filter filter : filters) {
filter.doFilter(msg);
}
System.out.println(msg);
}
}
架构类似于:
此时只是向list中添加,然后遍历执行,其实并不是一条链,只是挨个执行而已,我们将过滤器串成一条链,
定义类,并将list封装到其中,方法包括向链中添加过滤器(add)、从头开始运行这条链(doFilter)、删除过滤器…:
class FilterChain{
List<Filter> filters = new ArrayList<>();
public void add(Filter filter)
{
filters.add(filter);
}
public void doFilter(Msg msg)
{
for (Filter filter : filters) {
filter.doFilter(msg);
}
}
//删除filter等等一系列方法...
}
main中修改为:
public class Main {
public static void main(String[] args) {
Msg msg = new Msg();
msg.setMsg("djsiaohfioja shit dsjakljf wtf is");
FilterChain filters = new FilterChain();
filters.add(new ShitFilter());
filters.add(new WtfFilter());
filters.doFilter(msg);
System.out.println(msg);
}
}
看到这里你或许有疑问,这有什么区别呢,不就是把list装到了FilterChain里了吗,没有区别啊。
区别在什么地方呢?
其实区别很大,当我们有两个FilterChain时,其实是可以将两个链条连在一起的。
在实现之前先做一个小小的修改,因为每次filters都要add,所以我们将这里改为链式编程模式,将FilterChain中的add方法修改,返回FilterChain对象,即返回当前对象本身。
public FilterChain add(Filter filter)
{
filters.add(filter);
return this;
}
之前我们如果要添加过滤器:
filters.add(new ShitFilter());
filters.add(new WtfFilter());
现在我们只需:
filters.add(new ShitFilter()).add(new WtfFilter());
是不是感觉简便了许多(o゚v゚)ノ
现在当我们有两条filterchiain时,该怎么连起来呢?首先我们模拟一下多链条:
定义
class FuckFilter implements Filter{
@Override
public void doFilter(Msg m) {
String r = m.getMsg();
r = r.replace("fuck","****");
m.setMsg(r);
}
}
class BitchFilter implements Filter{
@Override
public void doFilter(Msg m) {
String r = m.getMsg();
r = r.replace("bitch","bbbbb");
m.setMsg(r);
}
}
定义两条链:
FilterChain filters = new FilterChain();
filters.add(new ShitFilter())
.add(new WtfFilter());
FilterChain filters2 = new FilterChain();
filters.add(new FuckFilter())
.add(new BitchFilter());
filters.doFilter(msg);
filters2.doFilter(msg);
filters与filters2怎么合并呢?
很简单,只需让FilterChain也实现Filter接口:
class FilterChain implements Filter{
List<Filter> filters = new ArrayList<>();
public FilterChain add(Filter filter)
{
filters.add(filter);
return this;
}
public void doFilter(Msg msg)
{
for (Filter filter : filters) {
filter.doFilter(msg);
}
}
//删除filter等等一系列方法...
}
现在我们只需要这样:
FilterChain filters = new FilterChain();
filters.add(new ShitFilter())
.add(new WtfFilter());
FilterChain filters2 = new FilterChain();
filters.add(new FuckFilter())
.add(new BitchFilter());
filters.add(filters2);
filters.add(filters2);
这样就实现了两个链条连在了一起。
那么它是怎么执行的呢?
这种调用类似于递归,当我们运行filters.doFilter(msg)
时,会执行:
//这里是filters的循环,会调用filters2的doFilter
for (Filter filter : filters) {
filter.doFilter(msg);
}
因为我们的filters2也实现了Filter接口,所以当执行到filters2时,调用filters2的doFilter(msg)方法,这样就会执行filters2中的过滤器:
//这里是filters2的循环,被filters调用
for (Filter filter : filters) {
filter.doFilter(msg);
}
这样我们不论写多少过滤器,都可以链成一条
我们再进一步改进,上面的过滤器都是一下子全部执行完,但是现在需要的是,思考这种场景:当执行到一个过滤器时,如果要是发现异常,就不要再往下再执行了,该怎么实现呢?
我们只需要将Filter中的doFilter方法返回值改为boolean类型:
interface Filter{
boolean doFilter(Msg m);
}
class ShitFilter implements Filter{
@Override
public boolean doFilter(Msg m) {
String r = m.getMsg();
r = r.replace("shit","ssss");
m.setMsg(r);
//这里可以做判断,如果不想让他往下执行,返回false.
return true;
}
}
//其他过滤器一样
然后我们修改FilterChain中的doFilter方法:
public boolean doFilter(Msg msg)
{
for (Filter filter : filters) {
if (!filter.doFilter(msg)) return false;
}
return true;
}
当其中一环返回false,那就返回false,不往下执行了。
思考题:servlet中的过滤器是这样的:
如何实现过滤器,使其处理request时是f1,f2,f3,f4的顺序,处理response时的顺序是f4,f3,f2,f1?
答案:设计模式之责任链模式思考题(servlet中filter过滤器是如何实现的?)