MENU

设计模式——责任链

September 9, 2018 • Read: 184 • 设计模式

问题引入

假设有一个论坛,很多人可以在上面发消息,但是这些消息,有的需要过滤敏感字眼,有的又需要修改关键字,举个简单的例子

String msg = "你好,澳门皇家赌场,<html>性感xx在线...</html>,:)";

就上面这消息,我们就有两处要进行屏蔽,首先是敏感字:“澳门皇家赌场,性感”,其次是html代码“<html>xxxxx</html>”要修改成“[html]xxxxx[/html]”,不然消息发出来可能会对结果产生影响。

针对上面的问题,我们通常都是这么做的

public class Main() {
    public static void main(String[] args) {
        String msg = "你好,澳门皇家赌场,<html>性感xx在线...</html>,:)";
        msg = msg.replace("澳门皇家赌场","").replace("性感","").replace("<","[").replace(">","]");
        System.out.println(msg);
    }
}

责任链

上面只是非常简单的一种写法,但是接下来我们要站在面向对象的角度去思考这个程序应该怎么写。马士兵老师说:“设计模式最基本的要求就是先写测试

信息过滤有两种,一种是html代码过滤,一种是敏感字过滤,所以我们干脆创建一个接口Filter,然后创建一个HtmlFilter类实现接口的doFilter方法,另一个SesitiveFilter类也实现doFilter方法

//Filter接口
public interface Filter {
    String doFilter(String str);
}
//HtmlFilter类
public class HtmlFilter implements Filter {
    
    @Override
    public String doFilter(String str) {
        String r = str.replace("<", "[").replace(">","]");
        return r;
    }
}
//SesitiveFilter类
public class SesitiveFilter implements Filter {

    @Override
    public String doFilter(String str) {
        String r = str.replace("澳门皇家赌场","").replace("性感","");
        return r;
    }
}
//MsgProcessor类
public class MsgProcessor {
    private String msg;
    Filter[] filters = {new HtmlFilter(),new SesitiveFilter()};
    public void set(String msg) {
        this.msg = msg;
    }
    public String get() {
        return msg;
    }
    public String process() {
        String r = msg;
        for(Filter f : filters)
            r = f.doFilter(r);
        return r;
    }
}

整个代码文件结构如下图所示:

这样写,有两个好处,假设我需要新增一个过滤规则,只需要新建一个类,实现接口的方法,然后将这个类增加到Filter数组中即可。还有一个好处,就是可以使过滤规则有一定的顺序,比方说一个新生来上学,他需要先交钱,再领书,再领被子,再进教室......Filter数组里匿名对象的顺序,就是执行的顺序。

可以用一幅图来表示责任链这种设计模式的过程:

黑色的箭头可以理解为一个消息的输入,经过三个黄色过滤器后,最终输出到数据库或者其他地方。

引入新的责任链

再考虑一个问题,假如又有一系列的责任链,需要将新的责任链插在原责任链的中间执行(具体见下图),应该怎么做?

用一个新的类FilterChain,里面有一个ArrayList,专门存放各种实现Filter的类

//FilterChain类
import java.util.*;
public class FilterChain {
    List<Filter> filters = new ArrayList<Filter>();
    public FilterChain add(Filter f) {
        this.filters.add(f);
        return this;
    }
    public String doFilter(String str) {
        String r = str;
        for(Filter f:filters) 
            r = f.doFilter(r);
        return r;
    }
}

MsgProcessor类也要修改

//MsgProcessor类
public class MsgProcessor {
    private String msg;
    //Filter[] filters = {new HtmlFilter(),new SesitiveFilter()};
    FilterChain fc;
    public FilterChain getFc() {
        return fc;
    }
    public void setFc(FilterChain fc) {
        this.fc = fc;
    }
    public void set(String msg) {
        this.msg = msg;
    }
    public String get() {
        return msg;
    }
    public String process() {
        return fc.doFilter(msg);
    }
}

最后是Main

//Main类
public class Main {
    public static void main(String[] args) {
        String msg = "你好,澳门皇家赌场,<html>性感xx在线...</html>,:)";
        MsgProcessor mp = new MsgProcessor();
        mp.set(msg);
        FilterChain fc = new FilterChain();
        fc.add(new HtmlFilter()).add(new SesitiveFilter());
        mp.setFc(fc);
        String result = mp.process();
        System.out.println(result);
    }
}

整个执行过程再讲述一遍,首先Main方法把msg传给MsgProcess,然后创建一个FilterChain对象,调用其add方法,往里面添加很多种Filter,然后将FilterChain对象也传给MsgProcess,最后调用MsgProcess中的process方法。process方法会执行fc.doFilter(msg),doFilter函数是将fc中的所有Filter执行一遍。具体执行流程可以看下图:

服务器客户端责任链

新的问题,客户端Client发送一个消息,经过Filter1,Filter2,然后传给服务器端Server。接下来服务器端要给一个消息反馈给客户端,因此要依次经过Filter2,Filter1(很明显,过去和回来经过Filter的顺序肯定是相反的)

这有点栈的思想在里面,将Client端发送出去时遇到的各种Filter压栈,然后在Server端传输回来的时候依次弹栈,类似的思想。

首先客户端和服务器端的类肯定要有

//Server端
public class ServerFilter {
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }
}
//Client端
public class ClientFilter {
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }
}
//Filter接口
public interface Filter {
    void doFilter(ClientFilter client,ServerFilter server,FilterChain chain);
}
//HtmlFilter类
public class HtmlFilter implements Filter {
    
    @Override
    public void doFilter(ClientFilter client,ServerFilter server,FilterChain chain) {
        client.setStr(client.getStr().replace("<", "[").replace(">","]") + "---HtmlFilter()");
        chain.doFilter(client, server, chain);
        server.setStr(server.getStr() + "---HtmlFilter()");
    }
}
//SesitiveFilter类
public class SesitiveFilter implements Filter {

    @Override
    public void doFilter(ClientFilter client,ServerFilter server,FilterChain chain) {
        client.setStr(client.getStr().replace("澳门皇家赌场","").replace("性感","") + "---SesitiveFilter()");
        chain.doFilter(client, server, chain);
        server.setStr(server.getStr() + "---SesitiveFilter()");
    }
}
//FilterChain类
import java.util.*;
public class FilterChain implements Filter{
    int idx = 0;
    List<Filter> filters = new ArrayList<Filter>();
    public FilterChain add(Filter f) {
        this.filters.add(f);
        return this;
    }
    @Override
    public void doFilter(ClientFilter client, ServerFilter server, FilterChain chain) {
        if(idx == filters.size())
            return;
        Filter f = filters.get(idx);
        idx++;
        f.doFilter(client, server, chain);
    }
}
MsgProcessor类
public class MsgProcessor {
    private String msg;
    //Filter[] filters = {new HtmlFilter(),new SesitiveFilter()};
    FilterChain fc;
    public FilterChain getFc() {
        return fc;
    }
    public void setFc(FilterChain fc) {
        this.fc = fc;
    }
    public void set(String msg) {
        this.msg = msg;
    }
    public String get() {
        return msg;
    }
    public void process(ClientFilter client,ServerFilter server) {
        fc.doFilter(client, server, fc);
    }
}
//Main类
public class Main {
    public static void main(String[] args) {
        String msg = "你好,澳门皇家赌场,<html>性感xx在线...</html>,:)";
        MsgProcessor mp = new MsgProcessor();
        FilterChain fc = new FilterChain();
        fc.add(new HtmlFilter()).add(new SesitiveFilter());
        mp.setFc(fc);
        ClientFilter client = new ClientFilter();
        client.setStr(msg);;
        ServerFilter server = new ServerFilter();
        server.setStr("Server");;
        mp.process(client, server);
        System.out.println(client.getStr());
        System.out.println(server.getStr());
    }
}

结语

马士兵老师说:“不要问学什么有用学什么没用,直接去招聘网站看看,现在招什么人才,需要会什么技术”

最后编辑于: October 10, 2018
Archives Tip
QR Code for this page
Tipping QR Code