Java设计模式之责任链
摘要:本笔记主要记录了责任链设计模式的设计、从问题的提出、到问绕解决问题的途径来展示责任链设计模式的思想与代码构建。
一:问题的引出
在实际项目中、我们常常需要对请求中所带来的信息进行处理。比如说我们经常会使用Filter来进行编码的处理、如果学过struts2、那么你对Interceptor一定不会陌生、struts2就是通过一系列的Interceptor将请求中的参数与request剥离开来供我们使用的。
下面会围绕论坛发帖的形式来使用Java实现责任链的设计模式。问题:当你在论坛发帖的时候、会不会有些时候某些内容是禁止发帖的?发了还会被封号?当然别想太多。问题简单化:如何对传进来的一个字符串进行一定的处理?
二:责任链的第一步实现
使用极限编程的形式:先写测试程序、再写相应的类。
1、测试类Main:
package com.chy.filter.test;
import com.chy.filter.MsgProcessor;
public class Main {
public static void main(String[] args) {
String msg = "大家好 :),<script>,被强迫, 敏感词汇,等等";
MsgProcessor mp = new MsgProcessor();
mp.setMsg(msg);
String result = mp.process();
System.out.println(result);
}
}
2、处理类MsgProcessor:
package com.chy.filter;
public class MsgProcessor {
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String process(){
msg = msg.replace("<", "[").replace(">", "]");
msg = msg.replace("被强迫", "无奈");
return msg;
}
}
处理类很简单、就是对Main类中的字符串进行两个处理:a)替换html标签。b)替换敏感词汇。
上述内容应该是很小菜一碟的事情、那么如果某天再想处理一下传入的字符串呢?处理的方式又想变一下呢?当然可以在MsgProcessor中加、但是有没有一种让我们加的比较方便、或者比较舒服的方式呢?简单的说就是:可以动态的指定过滤的方式和规则。具体的实现:模拟Servlet中Filter的实现。
1、定义一个专门用于处理字符串的接口——Filter、他提供一个方法doFilter(Stringstr)用于处理字符串。
package com.chy.filter;
public interface Filter {
public String doFilter(String str);
}
2、如果我们想对某一字符串进行特定的处理的时候、比如处理字符串中HTML标签、则我们可以定义一个HTMLFilter实现Filter、重写doFilter方法:
package com.chy.filter;
public class HTMLFilter implements Filter {
@Override
public String doFilter(String str) {
//process the html tag<>
String r = str.replace("<", "[").replace(">", "]");
return r;
}
}
3、同样的对于过滤敏感词汇的处理、我们可以建立一个SensitiveFilter、重写doFilter方法:
package com.chy.filter;
public class SesitiveFilter implements Filter{
@Override
public String doFilter(String str) {
//process the sensitive words
str = str.replace("被强迫", "无奈");
return str;
}
}
4、在MsgProcessor中调用这两个Filter处理msg、为了以后能使用指定的Filter来过滤字符串、我们可以在MsgProcessor中定义一个Filter数组、将我们想要使用的Filter都存放在这个数组中、然后在处理字符串的地方依次调用Filter来过滤、这样就达到了使用指定规则、指定方式过滤的目的、在项目中我们更加灵活的可以使用配置文件来指定这些Filter!MsgProcessor代码:
package com.chy.filter;
public class MsgProcessor {
private String msg;
private Filter[] filters = {new HTMLFilter(), new SesitiveFilter()};
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String process(){
for(Filter filter : filters){
msg = filter.doFilter(msg);
}
return msg;
}
}
上面的一个个对字符串的过滤器连接起来是不是很像一个链条?链条的环就是由一个一个过滤器组成的、并且我们可以随意组装这些环、不喜欢那个还可以直接抽掉。这样就在原来的基础上只修改Filter数组的情况下实现了对字符串的过滤。如果使用配置文件、那就更爽了、想怎么指定就怎么指定、而不用修改MsgProcessor的代码;
三:责任链的进一步实现
到前面会不会觉得责任链就已经很爽了?下面考虑这样一个问题:如果说我原来有一个Filter数组用来处理字符串了、那么我现在又有一个新的Filter链同样是处理字符串的、我想直接把新的Filter链加入到原来Filter数组的指定位置、怎么弄?
当然我们可能会想到直接copy代码、copy代码可以解决、如果有一万那、每次是不是要copy一万下?也有可能想到使用一个新的ArrayList、将两个Filter链全都加进来、是一种方法、还有没有更好的呢?
1、使用FilterChain!并且这个FilterChain同样实现了Filter接口——FilterChain:
package com.chy.filter;
import java.util.ArrayList;
import java.util.List;
public class FilterChain implements Filter{
//用于存放所有Filter
List<Filter> filters = new ArrayList<Filter>();
//添加Filter
public FilterChain addFilter(Filter f){
filters.add(f);
return this;
}
//移除一个Filter
public void removeFilter(Filter f){
filters.remove(f);
}
//使用这个FilterChain来过滤字符串
public String doFilter(String str){
for(Filter filter : filters){
str = filter.doFilter(str);
}
return str;
}
}
2、这样的话、在MsgProcessor中就不必再手动的new Filter数组了。直接定义一个FilterChain、并提供get、set方法。这样我们在使用MsgProcessor的时候就可以设置FilterChain了、并调用process方法处理字符串。
package com.chy.filter;
public class MsgProcessor {
private String msg;
private FilterChain filterChain;
public FilterChain getFilterChain() {
return filterChain;
}
public void setFilterChain(FilterChain filterChain) {
this.filterChain = filterChain;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String process(){
return filterChain.doFilter(msg);
}
}
3、这样就很简单的实现了我们上面那种将一个FilterChain添加到另一个FilterChain中来解决在一个FilterChain中添加另一个FilterChain的功能了。Main代码:
package com.chy.filter;
public class Main {
public static void main(String[] args) {
String msg = "大家好 :),<script>,被强迫, 敏感词汇,等等";
MsgProcessor mp = new MsgProcessor();
mp.setMsg(msg);
//构造第一个FilterChain
FilterChain fc = new FilterChain();
fc.addFilter(new HTMLFilter()).addFilter(new SesitiveFilter());
//构造第二个FilterChain、并且将新的FilterChain放入原来的FilterChain中。
FilterChain fc2 = new FilterChain().addFilter(new FaceFilter());
fc.addFilter(fc2);
mp.setFilterChain(fc);
String result = mp.process();
System.out.println(result);
}
}
result:
大家好 ^_^,[script],无奈, 敏感词汇,等等
四:责任链的再进一步实现
问题到上面视乎得到了很好的解决、但是实际开发中、我们一般从客户端发送消息到服务器端、此时我们使用一个FilterChain来处理request、当服务器端处理完成之后一般会给客户端一个反馈、response、那么我们怎么来过滤这个response呢?最直接的就是再写一个FilterChain啊、当然可以、只是这样的结构是不是有一点点的不完美呢?能不能这个FilterChain既能处理客户端发往服务器端的消息、也可以处理服务器端返回的消息呢?
下面通过模拟web中对request、response的处理来看看如何实现的
1、新建一个项目java_web_filter
2、定义两个类:Request、Response、这两个类在实际项目中包含有非常多的信息、简单起见、我们只在其中分别定义一个字符串、来表示其包含的信息——Reques、Response代码:
package com.chy.filter.web;
public class Request {
private String requestStr;
public String getRequestStr() {
return requestStr;
}
public void setRequestStr(String requestStr) {
this.requestStr = requestStr;
}
}
package com.chy.filter.web;
public class Response {
private String responseStr;
public String getResponseStr() {
return responseStr;
}
public void setResponseStr(String responseStr) {
this.responseStr = responseStr;
}
}
3、定义一个Filter接口、用于处理Request、Response对象中的我们自己定义的字符串——Filter代码:
package com.chy.filter.web;
public interface Filter {
public void doFilter(Request request, Response response);
}
4、同样使用FilterChain的方式来进行链式处理——FilterChain代码:
package com.chy.filter.web;
import java.util.ArrayList;
import java.util.List;
public class FilterChain implements Filter{
//用于存放所有Filter
List<Filter> filters = new ArrayList<Filter>();
int index = 0;
//添加Filter
public FilterChain addFilter(Filter f){
filters.add(f);
return this;
}
//移除一个Filter
public void removeFilter(Filter f){
filters.remove(f);
}
//使用这个FilterChain来过滤字符串
public void doFilter(Request request, Response response) {
for(Filter filter : filters){
filter.doFilter(request, response);
}
}
}
5、定义一个处理字符串的HTML标签的Filter——HTMLFilter代码:
package com.chy.filter.web;
public class HTMLFilter implements Filter {
@Override
public void doFilter(Request request, Response response) {
request.setRequestStr(request.getRequestStr().replace("<", "[").replace(">", "]")+"--------HTMLFilter");
}
}
6、定义一个处理敏感词汇的Filter——SensitiveFilter代码:
package com.chy.filter.web;
public class SesitiveFilter implements Filter{
@Override
public void doFilter(Request request, Response response) {
request.setRequestStr(request.getRequestStr().replace("被强迫", "无奈") + "------SensitiveFilter");
}
}
7、直接在Main方法中调用FilterChain对Request、Response进行处理——Main代码:
package com.chy.filter.web;
public class Main {
public static void main(String[] args) {
String msg = "大家好 :),<script>,被强迫, 敏感词汇,等等";
Request request = new Request();
request.setRequestStr(msg);
Response response = new Response();
response.setResponseStr(msg);
FilterChain fc = new FilterChain();
fc.addFilter(new HTMLFilter()).addFilter(new SesitiveFilter());
fc.doFilter(request, response);
System.out.println(request.getRequestStr());
System.out.println(response.getResponseStr());
}
}
从这里我们依然没有看到对Response的处理。下面开始处理Response、这里需要用到一个小技巧。
思想是这样的:当我们调用第一个Filter处理完Request之后、应该调用下一个Filter来处理由上面一个Filter传递过来的Request、同样下一个Filter处理完之后调用下下一个来处理。直到所有Filter处理完之后开始从最后一个Filter开始处理Response、最后一个处理完之后退回上一层Filter处理Response…一直到最开始一个Filter来处理Response。这样走下来之后就会发现对Request的处理是正序的、对Response的处理是反序的。
思想有了、接下来就是具体的实现过程了。
1、首先要解决的是上一个Filter处理完之后、如何把Request、Response传递给下一个Filter?为解决这个问题、此时不由的想到FilterChain中包含了我们所有的Filter、所以我们可以在Filter定义的doFilter方法中将FilterChain传递下去。
2、那这样的话在FilterChain中就要做两件事:
a) 判断是不是还有下一个Filter、有则调用下一个的Filter的doFilter处理并继续传递、没有则返回。
b) 改变Filter的处理方式、获取下一个Filter、调用其doFilter(xxx)处理并传递参数。
3、使用的时候、我们的任务就是构造FilterChain、填充FilterChain的Filter数组、调用其doFilter方法来处理Request、Response。
4、具体代码实现:
a) 新型Filter、其doFilter方法中包含FilterChain:
package com.chy.filter.web;
public interface Filter {
public void doFilter(Request request, Response response, FilterChain chain);
}
b) Filter的实现类HTMLFilter、SensitiveFilter、用于组装FilterChain——HTMLFilter代码:SensitiveFilter代码:
package com.chy.filter.web;
public class HTMLFilter implements Filter {
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
request.setRequestStr(request.getRequestStr().replace("<", "[").replace(">", "]")+"--------HTMLFilter");
chain.doFilter(request, response, chain);
response.setResponseStr(response.getResponseStr()+"--------HTMLFilter");
}
}
package com.chy.filter.web;
public class SesitiveFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
request.setRequestStr(request.getRequestStr().replace("被强迫", "无奈") + "------SensitiveFilter");
chain.doFilter(request, response, chain);
response.setResponseStr(response.getResponseStr()+"--------SensitiveFilter");
}
}
c) FilterChain:
package com.chy.filter.web;
import java.util.ArrayList;
import java.util.List;
public class FilterChain implements Filter{
//用于存放所有Filter
List<Filter> filters = new ArrayList<Filter>();
int index = 0;
//添加Filter
public FilterChain addFilter(Filter f){
filters.add(f);
return this;
}
//移除一个Filter
public void removeFilter(Filter f){
filters.remove(f);
}
//使用这个FilterChain来过滤字符串
public void doFilter(Request request, Response response, FilterChain chain) {
if(index == filters.size()){
return;
}
Filter f = filters.get(index);
index ++;
f.doFilter(request, response, chain);
}
}
d) Main:
package com.chy.filter.web;
public class Main {
public static void main(String[] args) {
String msg = "大家好 :),<script>,被强迫, 敏感词汇,等等";
Request request = new Request();
request.setRequestStr(msg);
Response response = new Response();
response.setResponseStr(msg);
//构造第一个FilterChain
FilterChain fc = new FilterChain();
fc.addFilter(new HTMLFilter()).addFilter(new SesitiveFilter());
fc.doFilter(request, response, fc);
System.out.println(request.getRequestStr());
System.out.println(response.getResponseStr());
}
}
运行结果:
大家好 :),[script],无奈, 敏感词汇,等等--------HTMLFilter------SensitiveFilter
大家好 :),<script>,被强迫, 敏感词汇,等等--------SensitiveFilter--------HTMLFilter
可以看到:处理Request、Response的顺序是相反的。
总结与补充:
1、总结:
到这里责任链设计模式(Chain Of Responsibility Pattern)也就告一段落了。体现了一种过滤和同时顺序处理请求和逆序处理响应的功能。struts2的Inteceptor就是以这种形式来实现的、层层拦截来达到一些功能。
2、补充:项目结构图
a) 第一个 : b)进一步: c)更近一步:
更多内容:Java设计模式之起始