Java设计模式之责任链

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设计模式之起始


 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值