前言
责任链模式(Chain Of Responsibility
)常用于解决这样的一些需求,用户想发布一些信息到论坛上,在这些信息进入数据库的过程中,要对信息的内容进行多方面的过滤,替换掉一些敏感词,敏感图片,或者直接驳回发布请求。在解决这些需求时,利用责任链模式,使得扩展性更强,更加符合开闭原则。在Java web
开发中,Filter
就使用了责任链模式。本文从一个具体需求出发,进行一步步改进,最后完成一个简单的责任链模式的Demo
。
需求
我们现在假设有这样一个类Post
里面包含了一个字符串message
,message
只包含A
、B
、C
、D
四种字符,每一个字符都代表了一种需要被替换的不合法信息,比如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());
}
}
目前现在整个系统的操作流程是这样的
现在让我们考虑一个问题,如果一些场景一下,我们需要把一个
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
各司其职完成相应的功能,此外易于扩展和重用,且能够及时中止流程。