The Chain Of Responsibility (1)
这篇文章介绍了责任链模式的设计思想及其在实际开发中的应用。从简单的问题出发,逐步展示了如何通过面向对象的思想和Filter接口的实现来构建一个响应式处理系统。文章详细描述了如何将多个过滤器整合到一个可扩展的框架中,并通过FilterChain实现了“链式”管理的方式。最终总结了责任链模式的核心思想及其在实际开发中的价值与应用场景。
今天分享一下,设计模式中的责任链模式,其余的不过多叙述。
思路
在正式学习责任链模式之前, 我们可以合理预判其最基础的形式应该就是一个线性数据结构.... 处理一件事务所需的基础架构确实类似于这样一个线性数据结构,这一现象本身已经能够说明问题. 需要指出的是,这些设计模式本质上是从对问题本质和内在规律的深入观察中自然演变而来的.
来源
责任链也是在实际开发过程中逐渐提炼出来的一个套路,在这种情况下被称作模式的原因所在。例如,在处理用户的上传数据时实施过滤功能时的做法是这样的:首先想到的是下面这种方法。
// 待处理的用户的输入数据
String message = "<script>while(1){alert('HaHa,敏感词,替换词')</script>";
String result = message.replaceAll("<","[");
result = result.replaceAll(">","]");
System.out.println("未被处理的数据为:" + message);
System.out.println("经过处理的数据为:" + result);
很简单了吧,但是想一想,这样真的够了吗?
该字符串类的设计遵循了一个链条式的结构。
这一特点体现在该方法$String(replaceAll)中的实现上。
加大点难度
设想我们引入一个更高级的功能模块。该模块将支持HTML代码转义、敏感词汇件过滤处理以及目标词替换等功能。如果继续沿用这种方式开发下去,“方法体”的规模将迅速扩大,并且维护起来会变得复杂。基于面向对象的理念,在这种情况下我们可以考虑抽象出一个接口。只要需要某个功能时,“功能模块名”的实现者就可以直接让其遵循该接口并完成相应的业务逻辑。“这样做就能满足需求。”
下面我们就来看看这种实现。
首先是接口:Filter.java
package ResponsibilityChain;
public interface Filter {
public String doFilte(String message);
}
HTMLFilter.java
package ResponsibilityChain;
/** * HTML filter utility.
* * 但是也添加了一些修改内容
* * @author Craig R. McClanahan
* @author Tim Tye
*/
public final class HTMLFilter implements Filter{
/** * Filter the specified message string for characters that are sensitive in
* HTML. This avoids potential attacks caused by including JavaScript codes
* in the request URL that is often reported in error messages.
* * @param message
* The message string to be filtered
*/
public String doFilte(String message) {
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuilder result = new StringBuilder(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());
}
}
SesitiveFilter.java
package ResponsibilityChain;
public final class SesitiveFilter implements Filter {
@Override
public String doFilte(String message) {
// 正常来说应该是个词库的,对词库中的数据进行匹配,这样比较合理一些,此处为了掩饰核心思想,就简化了这个操作
String str = message.replaceAll("敏感词", "不敏感了");
return str;
}
}
ReplacableFilter.java
package ResponsibilityChain;
public class ReplacableFilter implements Filter {
@Override
public String doFilte(String message) {
// 正常来说应该是个词库的,对词库中的数据进行匹配,这样比较合理一些,此处为了掩饰核心思想,就简化了这个操作
String str = message.replaceAll("替换词", "已被替换");
return str;
}
}
接着引入了一个业务逻辑管理器。它负责接收需要过滤的数据,并返回处理后的数据。
package ResponsibilityChain;
public class MyProcessor {
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String process() {
// 调用相关的过滤器进行词汇的过滤
HTMLFilter htmlFilter = new HTMLFilter();
String result = htmlFilter.doFilte(msg);
// 调用敏感词过滤器
SesitiveFilter sesitiveFilter = new SesitiveFilter();
result = sesitiveFilter.doFilte(result);
ReplacableFilter replacableFilter = new ReplacableFilter();
result = replacableFilter.doFilte(result);
return result;
}
}
最后来个测试吧。Main.java
package ResponsibilityChain;
public class Main {
public static void main(String[] args) {
// 待处理的用户的输入数据
String message = "<script>while(1){alert('HaHa,敏感词,替换词')</script>";
MyProcessor myProcessor = new MyProcessor();
myProcessor.setMsg(message);
String result = myProcessor.process();
System.out.println("未被处理的数据为:" + message);
System.out.println("经过处理的数据为:" + result);
}
}
小总结
通过这种方式即可实现所需功能
我们可以将该过滤器添加到我们的MyProcessor类中 从而全面实现我们的功能需求
但是看一下是否可行?
public String process() {
// 调用相关的过滤器进行词汇的过滤
HTMLFilter htmlFilter = new HTMLFilter();
String result = htmlFilter.doFilte(msg);
// 调用敏感词过滤器
SesitiveFilter sesitiveFilter = new SesitiveFilter();
result = sesitiveFilter.doFilte(result);
ReplacableFilter replacableFilter = new ReplacableFilter();
result = replacableFilter.doFilte(result);
return result;
}
这段代码显得略显冗余,给人的感受是稍微有些笨拙。接下来,我们就着手解决这一问题
责任链前身
为了解决现有代码中存在冗余问题,在责任链体系中是一个基础概念建议采用一个数组来统一存储所有Filter的实现类,并以有效管理过滤任务的目的。经过分析后发现只需对MyProcessor.java进行修改即可。
package ResponsibilityChain;
public class MyProcessor2 {
Filter[] filters = { new HTMLFilter(), new SesitiveFilter(), new ReplacableFilter() };
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String process() {
String result = msg;
for (Filter f : filters) {
result = f.doFilte(result);
}
return result;
}
}
测试一下:
package ResponsibilityChain;
public class Main2 {
public static void main(String[] args) {
// 待处理的用户的输入数据
String message = "<script>while(1){alert('HaHa,敏感词,替换词')</script>";
MyProcessor2 myProcessor2 = new MyProcessor2();
myProcessor2.setMsg(message);
String result = myProcessor2.process();
System.out.println("未被处理的数据为:" + message);
System.out.println("经过处理的数据为:" + result);
}
}
小总结:通过这一操作可以看出采用数组来进行管理的好处十分明显吧。具体来说,在每当我们需要添加新的过滤器实现类时,在这个数组中就必须确保其正确配置。看起来确实挺有意思的吧。
责任链出山
阅读了上面的代码之后,请问您是否已经掌握了其中的业务逻辑?请问您是否意识到这还不是我们采用的责任链模式?虽然它具备一定的基础功能但并没有体现出'链'这一核心特点如果我们有两个类似的模块需要协同工作那么单纯依靠单个模块处理显然是不够的因为当其中一个模块处理到一半时就需要插入另一个模块来共同完成任务这种情况下工作流程会变得异常复杂所以接下来我们需要将该功能交付给责任链来进行统一管理。
package ResponsibilityChain;
import java.util.ArrayList;
import java.util.List;
public class FilterChain implements Filter{
private List<Filter> filters = new ArrayList<Filter>();
/** * return itself for better usage the Chain
* * @param f
* @return
*/
public FilterChain addFilter(Filter f) {
filters.add(f);
return this;
}
public void remove(Filter f) {
filters.remove(f);
}
/** * For the chain , it's also a chain for filter
* * @param message
* message need to be filtered
* @return
*/
public String doFilte(String message) {
String result = message;
for (Filter f : filters) {
result = f.doFilte(result);
}
return result;
}
}
这样一来,MyProcessor也要进行修改,如MyProcessor3.java
package ResponsibilityChain;
public class MyProcessor3 {
private FilterChain filterChain;
public FilterChain getFilterChain() {
return filterChain;
}
public void setFilterChain(FilterChain filterChain) {
this.filterChain = filterChain;
}
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
// 此处为核心and关键
public String process() {
String result = msg;
result = filterChain.doFilte(result);
return result;
}
}
再来测试一把,Main3.java
package ResponsibilityChain;
public class Main3 {
public static void main(String[] args) {
// 待处理的用户的输入数据
String message = "<script>while(1){alert('HaHa,敏感词,替换词')</script>";
MyProcessor3 myProcessor3 = new MyProcessor3();
myProcessor3.setMsg(message);
FilterChain filterChain = new FilterChain();
// filterChain.addFilter(new HTMLFilter());
// filterChain.addFilter(new SesitiveFilter());
// filterChain.addFilter(new ReplacableFilter());
myProcessor3.setFilterChain(filterChain);
// 作为一个链的方式进行添加过滤器,这就是链式编程的好处
filterChain.addFilter(new HTMLFilter()).addFilter(new SesitiveFilter()).addFilter(new ReplacableFilter());
String result = filterChain.doFilte(message);
System.out.println("未被处理的数据为:" + message);
System.out.println("经过处理的数据为:" + result);
}
}
因此,在经典的思维方式往往能够起到启发作用。
是否曾留意到在FilterChain.java中存在哪些有趣的特性?
因此,在经典的思维方式往往能够起到启发作用。
是否曾留意到在FilterChain.java中存在哪些有趣的特性?
public FilterChain addFilter(Filter f) {
filters.add(f);
return this;
}
当该方法返回自身时, 就可以持续调用同样的方法, 并以连锁的方式完成任务. 此外, 在FilterChain类中也定义了一个doFilte()函数, 并将其过滤功能从MyProcessor.java类转移到了此处.
而且通过开发出了一个功能完善的FilterChain组件,并不意味着就只能进行基本的过滤操作;实际上还可以自由地为每个数据流定制特定的过滤规则。
这就是面向对象的核心理念;任何对象的主要职责都由其自身属性所决定。
通过这种方式,在MyProcessor3.java中我们就可以实现责任链的过滤机制。
总结
本文基于一个现实中的实际问题切入,在逐步推进的过程中经历了由简单朴素的思想逐步提升到面向对象的方法,并在经典责任链思想的引导下完成了对一个简单责任链模式的具体实现。
如果细心琢磨一番后,总能发现不少值得反思的地方。这种责任链模式在实际开发过程中也很常见,在像Struts 2这样的框架中就可以见到它的身影。
