责任链模式

前言

责任链模式(Chain Of Responsibility)常用于解决这样的一些需求,用户想发布一些信息到论坛上,在这些信息进入数据库的过程中,要对信息的内容进行多方面的过滤,替换掉一些敏感词,敏感图片,或者直接驳回发布请求。在解决这些需求时,利用责任链模式,使得扩展性更强,更加符合开闭原则。在Java web开发中,Filter就使用了责任链模式。本文从一个具体需求出发,进行一步步改进,最后完成一个简单的责任链模式的Demo

需求

我们现在假设有这样一个类Post里面包含了一个字符串messagemessage只包含ABCD四种字符,每一个字符都代表了一种需要被替换的不合法信息,比如A表示脏话等。我们的目标是在这个Post被存进数据库之前,把这些非法字符都替换为字符N(normal)。

  • Post
class Post {
    private String message;
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
}

这个需求太简单了,我们只要分别对这4个字符进行单独处理就好了。

  • 简单处理
public class CORTest {
    public static void main(String[] args) {
        Post post = new Post();
        post.setMessage("ANNDNDDBBBCCAA");

        String msg = post.getMessage();

        msg = msg.replaceAll("A", "N");
        msg = msg.replaceAll("B", "N");
        msg = msg.replaceAll("C", "N");
        msg = msg.replaceAll("D", "N");

        post.setMessage(msg);
        System.out.println(post.getMessage());
    }
}

  • 输出结果
NNNNNNNNNNNNNN

Process finished with exit code 0

虽然完全能满足我们的需求,但我们要知道在这里对问题做了简单的抽象,实际应用中对每一类非法信息的处理都可能是非常复杂的,我们把这么多复杂的处理逻辑全部罗列到一个类中,维护起来非常复杂,且往往我们之后会有新的信息类型需要进行过滤,那么就需要在这个类的基础上进行添加修改,可扩展性很差。现在让我们将替换一类不合法信息的操作抽取为Filter接口中的doFilter()方法,再让对于特定字符的替换操作抽取成一个单独的类,且实现这个Filter接口。

改进:抽象出Filter接口

  • Filter接口与具体实现类
interface Filter {
    void doFilter(Post post);
}

class AFilter implements Filter {
    @Override
    public void doFilter(Post post) {
        String msg = post.getMessage();
        msg = msg.replaceAll("A", "N");
        post.setMessage(msg);
    }
}
class BFilter implements Filter {
    @Override
    public void doFilter(Post post) {
        String msg = post.getMessage();
        msg = msg.replaceAll("B", "N");
        post.setMessage(msg);
    }
}
class CFilter implements Filter {
    @Override
    public void doFilter(Post post) {
        String msg = post.getMessage();
        msg = msg.replaceAll("C", "N");
        post.setMessage(msg);
    }
}
class DFilter implements Filter {
    @Override
    public void doFilter(Post post) {
        String msg = post.getMessage();
        msg = msg.replaceAll("D", "N");
        post.setMessage(msg);
    }
}

封装了这些处理类后,我们对Post中的非法信息的替换处理过程如下

public class CORTest {
    public static void main(String[] args) {
        Post post = new Post();
        post.setMessage("ANNDNDDBBBCCAA");

        Filter afilier = new AFilter();
        Filter bfilier = new BFilter();
        Filter cfilier = new CFilter();
        Filter dfilier = new DFilter();

        afilier.doFilter(post);
        bfilier.doFilter(post);
        cfilier.doFilter(post);
        dfilier.doFilter(post);


        System.out.println(post.getMessage());
    }
}

看起来好像和之前一样,但是现在些实现了Filter接口的类可以在别的地方进行重用,也算是一种改进。进一步我们发现,既然每一种Filter都会被调用来进行信息的替换处理工作,我们干脆将其组织成一个队列,然后直接遍历这个列表,对其中的每一个Filter调用doFilter(post)操作就可以了。

包装成FilterChain

  • 组织成一个队列
public class CORTest {
    public static void main(String[] args) {
        Post post = new Post();
        post.setMessage("ANNDNDDBBBCCAA");

        Filter afilier = new AFilter();
        Filter bfilier = new BFilter();
        Filter cfilier = new CFilter();
        Filter dfilier = new DFilter();

        ArrayList filters = new ArrayList<>();
        filters.add(afilier);
        filters.add(bfilier);
        filters.add(cfilier);
        filters.add(dfilier);

        for (Filter filter : filters) {
            filter.doFilter(post);
        }
        System.out.println(post.getMessage());
    }
}

让我们进一步的把这个队列也抽取出来,因为一个Filter调用完后再调用下一个Filter,这些Filter就像被组织成了一条链,我们就给它取名FilterChain吧,此外我们之前提到的扩展性问题在这里也很好实现,我们只要给这个FilterChain里增加一个add()方法,当有新的Filter需要添加进FilterChain这个类的时候,就调用这个方法好了。

  • FilterChain
class FilterChain {
    private List filters;

    public FilterChain(){
        this.filters = new ArrayList<>();
    }
    public FilterChain add(Filter filter) {
        filters.add(filter);
        return this;
    }
    public void doFilter(Post post) {
        for (Filter filter : filters) {
            filter.doFilter(post);
        }
    }
}
  • 使用FilterChain解决非法信息的过滤替换操作
public class CORTest {
    public static void main(String[] args) {
        Post post = new Post();
        post.setMessage("ANNDNDDBBBCCAA");

        Filter afilier = new AFilter();
        Filter bfilier = new BFilter();
        Filter cfilier = new CFilter();
        Filter dfilier = new DFilter();

        FilterChain filterChain = new FilterChain();
        filterChain.add(afilier)
                .add(bfilier)
                .add(cfilier)
                .add(dfilier);

        filterChain.doFilter(post);

        System.out.println(post.getMessage());
    }
}

目前现在整个系统的操作流程是这样的

责任链模式_第1张图片
执行流程

现在让我们考虑一个问题,如果一些场景一下,我们需要把一个 FilterChain和另一个 FilterChain合并,比如说把 ChainA追加到 ChainB的尾部,有什么比较好的实现方法吗?我们现在这里的 add操作只能增加实现了 Filter接口的类实例,我们要再添加一个 addChain方法吗?其实不用这么复杂,注意到 FilterChain里已经实现了 doFilter()方法,那我们只要把 FilterChain也看作是一种 Filter,不就可以直接调用 add方法了吗?所以我们只要把 FilterChain也实现 Filter接口就可以了。

  • FilterChain也实现Filter接口
class FilterChain implements Filter {
    private List filters;

    public FilterChain(){
        this.filters = new ArrayList<>();
    }
    public FilterChain add(Filter filter) {
        filters.add(filter);
        return this;
    }
    public void doFilter(Post post) {
        for (Filter filter : filters) {
            filter.doFilter(post);
        }
    }
}

改进:及时中止

让我们完善下上述机制,假设在这个FilterChain 中的某一环,我们的Filter检测到了诈骗信息,这时就不是进行替换操作这么简单了,我们应当立即中止操作,直接返回一个错误状态,驳回本次上传请求。实现的方式也很简单,我们只要在每次doFilter中加入一个判断逻辑,如果返回 true,则表示可以继续,返回false则表示立即中止,现在我们假设字符C表示一类我们无法容忍,需要驳回上传请求的非法逻辑,让我立即中止本次上传操作。

  • 加入中止功能
class FilterChain implements Filter {
    private List filters;

    public FilterChain(){
        this.filters = new ArrayList<>();
    }
    public FilterChain add(Filter filter) {
        filters.add(filter);
        return this;
    }
    public boolean doFilter(Post post) {
        for (Filter filter : filters) {
            if (!filter.doFilter(post)) return false;
        }
        return true;
    }
}

class Post {
    private String message;
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

interface Filter {
    boolean doFilter(Post post);
}

class AFilter implements Filter {
    @Override
    public boolean doFilter(Post post) {
        String msg = post.getMessage();
        msg = msg.replaceAll("A", "N");
        post.setMessage(msg);
        return true;
    }
}
class BFilter implements Filter {
    @Override
    public boolean doFilter(Post post) {
        String msg = post.getMessage();
        msg = msg.replaceAll("B", "N");
        post.setMessage(msg);
        return true;
    }
}
class CFilter implements Filter {
    @Override
    public boolean doFilter(Post post) {
        String msg = post.getMessage();
        if (msg.indexOf("C") != -1) return false;
        return true;
    }
}
class DFilter implements Filter {
    @Override
    public boolean doFilter(Post post) {
        String msg = post.getMessage();
        msg = msg.replaceAll("D", "N");
        post.setMessage(msg);
        return true;
    }
}

再进行一次

NNNDNDDNNNCCNN

Process finished with exit code 0

可以发现当执行到处理C字符的Filter的时候,流程就中断了,因此D也未得到处理。到目前为止我们已经实现了一个简单的责任链模式,链中的每一个Filter各司其职完成相应的功能,此外易于扩展和重用,且能够及时中止流程。

你可能感兴趣的:(责任链模式)