本文最后更新于:3 years ago
责任链模式算是设计模式中比较难的模式了,所以这里会比较详细的介绍一下这个模式。
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于 行为型模式 。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。结构型模式
如果想了解责任链模式模式的具体的介绍,菜鸟教程介绍得比较详细↓
菜鸟教程-责任链模式
结构图
优缺点
优点:
1、降低耦合度。它将请求的发送者和接收者解耦。
2、简化了对象。使得对象不需要知道链的结构。
3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
4、增加新的请求处理类很方便。
缺点:
1、不能保证请求一定被接收。
2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
3、可能不容易观察运行时的特征,有碍于除错。
使用场景
1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
3、可动态指定一组对象处理请求。
实现代码
提出需求,有一些基础的应该知道,客户端在向服务端发送请求的时候,是存在过滤的(为什么要过滤?为了防止黑客攻击,例如xss)。
而这次我们提出的需求就是,用户在向服务器端提交的时候,我们对提交的东西,进行匹配替换。
简单代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| Code1 class Msg{ private String name; private String msg;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getMsg() { return msg; }
public void setMsg(String msg) { this.msg = msg; }
@Override public String toString() { return "Msg{" + "name='" + name + '\'' + ", msg='" + msg + '\'' + '}'; } }
Main public class Main { public static void main(String[] args) { Msg msg = new Msg(); msg.setMsg("插播一条消息<script>:本网站已经被我Hacker了,交钱信息不外漏:),联系方式:xf.com");
String r = msg.getMsg(); r = r.replace('<', '['); r = r.replace('>', ']'); r = r.replaceAll("Hacker", " "); msg.setMsg(r); System.out.println(msg); } }
|
结果如下:
| Msg{name='null', msg='插播一条消息[script]:本网站已经被我 了,交钱信息不外漏:),联系方式:xf.com'}
|
但弊端也是很明显的,对于过滤这个动作不确定,可能以后会再添加其他的过滤,这时候该怎么做呢?
第一反应肯定是直接在后面添加过滤语句,这一定是可以的。但是这里的代码其实是很简单,真正的业务可能每一步都很复杂,例如过滤字符串可能要过滤很多
如果将所有代码都写在一块,首先非常乱,其次不容易扩展。
软件可扩展性指添加新功能的时候,对原来的软件架构改动越少,说明扩展性越好
封装->变化(要处理的逻辑,这里是要处理的字符串的逻辑可能会变)
解决思路是,前面学过了这么多模式,不难想出,我们将过滤这个动作封装成一个接口,里面含有过滤方法。
然后将不同的过滤机制实现这个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| Code2 class Msg{ private String name; private String msg;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getMsg() { return msg; }
public void setMsg(String msg) { this.msg = msg; }
@Override public String toString() { return "Msg{" + "name='" + name + '\'' + ", msg='" + msg + '\'' + '}'; } }
interface Filter{ void doFilter(Msg msg); }
class HTMLFilter implements Filter{
@Override public void doFilter(Msg msg) { String r = msg.getMsg(); r = r.replace('<', '['); r = r.replace('>', ']'); msg.setMsg(r); } }
class SensitiveFilter implements Filter{
@Override public void doFilter(Msg msg) { String r = msg.getMsg(); r = r.replaceAll("Hacker", " "); msg.setMsg(r); } }
Main public class Main { public static void main(String[] args) { Msg msg = new Msg(); msg.setMsg("插播一条消息<script>:本网站已经被我Hacker了,交钱信息不外漏:),联系方式:xf.com");
List<Filter> filters = new ArrayList<>(); filters.add(new HTMLFilter()); filters.add(new SensitiveFilter());
for (Filter filter : filters) { filter.doFilter(msg); } System.out.println(msg); } }
|
输出
| Msg{name='null', msg='插播一条消息[script]:本网站已经被我 了,交钱信息不外漏:),联系方式:xf.com'}
|
现在,这些过滤就可以看出是一条责任链
这里我们再完善一下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| Code3 class Msg{ private String name; private String msg;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getMsg() { return msg; }
public void setMsg(String msg) { this.msg = msg; }
@Override public String toString() { return "Msg{" + "name='" + name + '\'' + ", msg='" + msg + '\'' + '}'; } }
interface Filter{ void doFilter(Msg msg); }
class HTMLFilter implements Filter{
@Override public void doFilter(Msg msg) { String r = msg.getMsg(); r = r.replace('<', '['); r = r.replace('>', ']'); msg.setMsg(r); } }
class SensitiveFilter implements Filter{
@Override public void doFilter(Msg msg) { String r = msg.getMsg(); r = r.replaceAll("Hacker", " "); msg.setMsg(r); } }
FilterChain class FilterChain{ List<Filter> filters = new ArrayList<>();
public FilterChain add(Filter filter){ filters.add(filter); return this; }
public void doFilter(Msg msg){ for (Filter f : filters) { f.doFilter(msg); } } }
Main public class Main { public static void main(String[] args) { Msg msg = new Msg(); msg.setMsg("插播一条消息<script>:本网站已经被我Hacker了,交钱信息不外漏:),联系方式:xf.com");
FilterChain fc = new FilterChain(); fc.add(new HTMLFilter()) .add(new SensitiveFilter());
System.out.println(msg); } }
|
Code2和Code3的区别并不大,但Code3的代码,实用性更高。
我们知道在过滤的时候肯定不止一条链对于数据的处理,实际上应该是多条链。那如何将这多条链同时来处理一条数据呢?
这里我们让FilterChain也实现Filter接口,这样我们就可以通过add()方法,将多条链链接在一起。
而此时就可以看出Code2和Code3之间的差距了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
| Code4 class Msg{ private String name; private String msg;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getMsg() { return msg; }
public void setMsg(String msg) { this.msg = msg; }
@Override public String toString() { return "Msg{" + "name='" + name + '\'' + ", msg='" + msg + '\'' + '}'; } }
interface Filter{ void doFilter(Msg msg); }
class HTMLFilter implements Filter{
@Override public void doFilter(Msg msg) { String r = msg.getMsg(); r = r.replace('<', '['); r = r.replace('>', ']'); msg.setMsg(r); } }
class SensitiveFilter implements Filter{
@Override public void doFilter(Msg msg) { String r = msg.getMsg(); r = r.replaceAll("Hacker", " "); msg.setMsg(r); } }
class FaceFilter implements Filter{
@Override public void doFilter(Msg msg) { String r = msg.getMsg(); r = r.replace(":)", "^V^"); msg.setMsg(r); } }
class URLFilter implements Filter{
@Override public void doFilter(Msg msg) { String r = msg.getMsg(); r = r.replace("xf.com", "https://xf.com"); msg.setMsg(r); } }
class FilterChain implements Filter{ List<Filter> filters = new ArrayList<>();
public FilterChain add(Filter filter){ filters.add(filter); return this; }
public void doFilter(Msg msg){ for (Filter f : filters) { f.doFilter(msg); } } }
Main public class Main { public static void main(String[] args) { Msg msg = new Msg(); msg.setMsg("插播一条消息<script>:本网站已经被我Hacker了,交钱信息不外漏:),联系方式:xf.com");
FilterChain fc = new FilterChain(); fc.add(new HTMLFilter()) .add(new SensitiveFilter()); FilterChain fc2 = new FilterChain(); fc2.add(new FaceFilter()).add(new URLFilter());
fc.add(fc2); fc.doFilter(msg); System.out.println(msg); }
}
|
这里我们就可以看出Code3就很好的将代码的数量大大减少。
到了现在,只差最后一点需求了,由FilterChain中的某一个Filter决定链条是否继续。
什么意思呢?先看一下图
当数据在Filter2处理完成时,发现含有敏感词,此时就不用Filter3处理就结束了。
但怎么实现呢?这个感觉还是比较复杂的
这个必须要在Filter内部进行过滤,是不能够写在FilterChain中的(影响扩展)
这里要将Filter接口中的 doFilter() 方法返回类型改为Boolean类型,那么在每一个实现Filter的方法中,
都需要返回Boolean值,然后判断如果为false,即退出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| Code5 class Msg{ private String name; private String msg;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getMsg() { return msg; }
public void setMsg(String msg) { this.msg = msg; }
@Override public String toString() { return "Msg{" + "name='" + name + '\'' + ", msg='" + msg + '\'' + '}'; } }
interface Filter{ Boolean doFilter(Msg msg); }
class HTMLFilter implements Filter{
@Override public Boolean doFilter(Msg msg) { String r = msg.getMsg(); r = r.replace('<', '['); r = r.replace('>', ']'); msg.setMsg(r); return true; } }
class SensitiveFilter implements Filter{
@Override public Boolean doFilter(Msg msg) { String r = msg.getMsg(); r = r.replaceAll("Hacker", " "); msg.setMsg(r); return false; } }
class FaceFilter implements Filter{
@Override public Boolean doFilter(Msg msg) { String r = msg.getMsg(); r = r.replace(":)", "^V^"); msg.setMsg(r); return true; } }
class URLFilter implements Filter{
@Override public Boolean doFilter(Msg msg) { String r = msg.getMsg(); r = r.replace("xf.com", "https://xf.com"); msg.setMsg(r); return true; } }
class FilterChain implements Filter{ List<Filter> filters = new ArrayList<>();
public FilterChain add(Filter filter){ filters.add(filter); return this; }
public Boolean doFilter(Msg msg){ for (Filter f : filters) { if (!f.doFilter(msg)) return false; } return true; } }
public class Main { public static void main(String[] args) { Msg msg = new Msg(); msg.setMsg("插播一条消息<script>:本网站已经被我Hacker了,交钱信息不外漏:),联系方式:xf.com");
FilterChain fc = new FilterChain(); fc.add(new HTMLFilter()) .add(new SensitiveFilter()); FilterChain fc2 = new FilterChain(); fc2.add(new FaceFilter()).add(new URLFilter());
fc.add(fc2); fc.doFilter(msg); System.out.println(msg); }
}
|
如果将SensitiveFilter类中返回值为true,则结果为:
| Msg{name='null', msg='插播一条消息[script]:本网站已经被我 了,交钱信息不外漏^V^,联系方式:https://xf.com'}
|
如果将SensitiveFilter类中返回值为false,则结果为:
| Msg{name='null', msg='插播一条消息[script]:本网站已经被我 了,交钱信息不外漏:),联系方式:xf.com'}
|
是不是观察出来什么不一样了,当SensitiveFilter类中返回false,那么此时就不会再去调用fc2中的Filter。
好了,到这里责任链模式,思想就基本上🔒完了,如果想比较深入了解的话,可以去看看Java EE中FilterChain的API,如果还有哪天比较空闲的话,我会把这个API的实现说一下,毕竟这也算是设计模式中难度第二的模式。