一、定义
责任链模式(Chain of Responsibility)的目标是使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
在处理用户的请求时可能要根据不同的情况对请求添加不同的处理逻辑,在这时候就可以利用责任链进行设计。当需要添加一个处理逻辑时可以很方便的添加一个处理的节点。
二、需求
对用户提交的消息内容进行过滤,将诸如:html标签、js代码、敏感词汇等内容进行过滤。实现类似servlet的Filter的功能。
三、实现
1.创建一个类似HttpServletRequest的类Request
/**
* 模拟HttpServletRequest
* @author xjh
*
*/
class Request{
/**
* 存储属性的集合
*/
private Map<String, Object> attributes = new HashMap<String, Object>();
/**
* 存入一个属性
* @param key 属性关键字
* @param value 属性值
*/
public void setAttribute(String key, Object value){
attributes.put(key, value);
}
/**
* 获取一个属性的值
* @param key 属性关键字
* @return 属性的值
*/
public Object getAttribute(String key){
return attributes.get(key);
}
/**
* 其他像removeAttribute等就不提供了,这里只用做模拟测试
*/
}
2.创建一个类似HttpServletResponse的类Response
/**
* 模拟HttpServletResponse
* @author xjh
*/
class Response{
/**
* 这里只做演示,加这个属性只是为了更好展现责任链模式
*/
private String attribute = "模拟消息头";
public void setHeader(String attribute){
this.attribute = attribute;
}
public String getAttribute(){
return this.attribute;
}
}
3.创建过滤器接口
/**
* 过滤器接口
* @author xjh
*
*/
interface Filter{
/**
* 进行过滤的方法
* @return
*/
public void doFilter(Request request, Response response, FilterChain chain);
}
4.创建对HTML元素进行过虑的HTMLFilter类
/**
* HTML过滤器,对html标签进行过滤
* @author xjh
*
*/
class HTMLFilter implements Filter{
/**
* 真正进行过滤的方法
* @param message
* @return
*/
private String filter(String message) {
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0); //字符串转字符数组
StringBuffer result = new StringBuffer(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
/**
* 模拟servlet的Filter类中的doFilter方法
*/
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
//得到消息内容
String msg = (String) request.getAttribute("msg");
//过滤消息内容
msg = filter(msg);
//为了显示过滤器的调用顺序加入的信息
msg += "<进入HTMLFilter过滤>";
request.setAttribute("msg", msg);
//继续执行下一个过滤器
chain.doFilter(request, response, chain);
//下面的代码为了显示每个过滤器的执行顺序
String str = response.getAttribute();
str += "<完成HTMLFilter过滤>";
response.setAttribute(str);
}
}
5.创建敏感词语过滤器SensitiveFilter类
/**
* 敏感词语过滤器,对一些敏感的词语进行过滤
* @author xjh
*/
class SensitiveFilter implements Filter{
private String[] sensitiveWords = {"傻逼","鸟人"};
/**
* 真正进行过滤的方法
* @param message
* @return
*/
private String filter(String message){
for(String word:sensitiveWords){
message = message.replaceAll(word, "?");
}
return message;
}
/**
* 模拟servlet的Filter类中的doFilter方法
*/
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
//得到消息内容
String msg = (String) request.getAttribute("msg");
//过滤消息内容
msg = filter(msg);
//为了显示过滤器的调用顺序加入的信息
msg += "<进入SensitiveFilter过滤>";
request.setAttribute("msg", msg);
//继续执行下一个过滤器
chain.doFilter(request, response, chain);
//下面的代码为了显示每个过滤器的执行顺序
String str = response.getAttribute();
str += "<完成SensitiveFilter过滤>";
response.setAttribute(str);
}
}
6.创建过滤器链FilterChain/** * 过滤链 * 由于过滤链本身可以看成是过滤器, * 过滤链是由单个过滤器组合而成,而这些单个过滤器的组合又可以看成一个整体,因为它同样也有doFilter * 这样,一个过滤器链中也可以加入某个过滤器链了 * @author xjh * */class FilterChain implements Filter{ /** * 过滤器集合 */ private List<Filter> filters = new ArrayList<Filter>(); /** * 给过滤器集合添加一个过滤器 * @param filter Filter实例 * @return 返回当前过滤器实例对象 */ public FilterChain addFilter(Filter filter){ filters.add(filter); return this; //为了能进行链式调用返回当前实例 } /** * 从过滤器集合中移除一个过滤器(通过Filter实例) * @param filter Filter实例 * @return 返回当前过滤器实例对象 */ public FilterChain removeFilter(Filter filter){ filters.remove(filter); return this; //为了能进行链式调用返回当前实例 } /** * 从过滤器集合中移除一个过滤器(通过索引位置) * @param index 索引位置 * @return 返回被移除的的Filter */ public Filter removeFilter(int index){ return filters.remove(index); } /** * 这个索引表示要执行哪个过滤器的下标 */ private int index = 0; /** * 模拟过滤链的doFilter方法 */ @Override public void doFilter(Request request, Response response, FilterChain chain) { //判断是否已经执行到最后一个过滤器了 if(index >= filters.size()){ return ; //如果执行到最后一个filter那就返回 } //否则,继续执行下一个filter filters.get(index++).doFilter(request, response, chain); } }
7.创建测试类ChainTest
/**
* 责任链模式测试
* 需求:
* 对发布的信息进行检查,信息可能有不良内容或js代码等,所以需要进行过滤。
*
* @author xjh
*
*/
public class ChainTest {
public static void main(String[] args) {
//模拟某人发布的信息
String msg = "这人真是:傻逼!跟他说了那么多次都不知道怎么做!请点击<a>这里</a>。<script>alert('哈哈')</script>";
//创建Request对象
Request request = new Request();
//模拟设置一个属性
request.setAttribute("msg", msg);
//创建一个Response对象
Response response = new Response();
response.setAttribute("模拟消息头");
//创建HTMLFilter实例
HTMLFilter htmlFilter = new HTMLFilter();
//创建Sensitive过滤器实例
SensitiveFilter sensitiveFilter = new SensitiveFilter();
//创建过滤链对象,并将每个过滤器加入(实际应用当中每个filter应该是从配置文件中读取并实例化,如web.xml中的filter)
FilterChain filterChain = new FilterChain();
filterChain.addFilter(htmlFilter).addFilter(sensitiveFilter);
//执行过滤
filterChain.doFilter(request, response, filterChain);
//显示被过滤后的消息内容
System.out.println(request.getAttribute("msg") + "\n" + response.getAttribute());
}
}
8.进行测试
测试结果如下图所示:
四、分析图
五、总结
优点:
•责任链模式降低了请求的发送端和接收端之间的耦合,使多个对象都有机会处理这个请求。
•由于是在客户端来定义责任链的结构,可以动态地增加或修改处理一个请求的结构,增强了给对象指派职责的灵活性。
缺点:
•责任链模式一般是从链子的开头位置进行遍历,找到时候的处理对象,对性能有一定的损耗。
适用场合:
•有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
•想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
•可处理一个请求的对象集合应被动态指定。
•当一个方法的传入参数将成为分支语句的判断条件,分支条件存在扩展的可能,每一个分支的职责相对独立,且逻辑较为复杂时。