是什么
策略模式属于行为型模式。创建多个对象,使这些对象形成一条链,并沿着这条链传递请求,直到链上的某一个对象决定处理此请求。
优缺点
使程序结构更加灵活,扩展性更强。
优点
- 降低耦合度,客户端不需要知道这个请求被谁处理了,而处理者也不需要知道各个处理者之间的传递关系。
- 扩展性强,新增处理者的时候,只需要继承父处理者,然后重写或者延用父处理者的业务逻辑方法。
缺点
- 请求会从链的头部一直向下传递,直到有处理者处理,那么有可能因为链过长导致系统性能减低。
- 请求可能是递归传递的。
怎么用
问题重现
现在需要为从客户端发起的请求做一个 referer 的防盗链,这个防盗链分为本地、测试和生产。每一种环境 referer 处理的正则表达式都不一样。
一般想法的话,是在一个类中写出所有的 正则表达式 ,然后循环判断。但是当新增一个环境的时候,就需要修改这个类,这样子 不符合开闭原则 。
一般代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.wsk.gateway.config;
import java.util.regex.Pattern;
/**
* @author wsk1103
* @description 描述
*/
public class Simple {
private static final Pattern DEV = Pattern.compile("^https?://(localhost|127.0.0.1)(/|$)");
private static final Pattern TEST = Pattern.compile("^https?://(test.wsk1103.com|192.168.1.1)(/|$)");
private static final Pattern REP = Pattern.compile("^https?://(wsk1103.com|192.168.2.1)(/|$)");
public static boolean doChain(String referer) {
return DEV.matcher(referer).find() || TEST.matcher(referer).find() || REP.matcher(referer).find();
}
}
当新增一个环境的时候,就需要改动这个代码。
使用责任链模式
设计抽象类
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
package com.wsk.gateway.config;
import java.util.regex.Pattern;
/**
* @author wsk1103
* @date 2019/6/24
* @description 抽象的处理器
*/
public abstract class AbstractRefererAction implements Comparable{
/**
* 链的优先级
*/
private int num;
/**
* 正则表达式
*/
private Pattern pattern;
protected AbstractRefererAction(int num, Pattern pattern) {
this.num = num;
this.pattern = pattern;
}
/**
* 处理逻辑,子类只需要提供正则表达式和优先级即可
* @param referer
* @return
*/
public boolean doChain(String referer) {
if (pattern == null) {
return false;
}
return pattern.matcher(referer).find();
}
@Override
public int compareTo(Object o) {
if (o instanceof AbstractRefererAction) {
return Integer.compare(this.num, ((AbstractRefererAction) o).getNum());
} else {
throw new IllegalArgumentException();
}
}
public int getNum() {
return this.num;
}
}
实现抽象类-开发,测试,生产
配合 spring 的 bean 管理,使所有实现类实例化并且单例化
开发环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.wsk.gateway.config;
import org.springframework.stereotype.Service;
import java.util.regex.Pattern;
/**
* @author wsk1103
* @date 2019/6/24
* @description 开发环境
*/
@Service
public class DevRefererAction extends AbstractRefererAction {
private static final Pattern PATTERN = Pattern.compile("^https?://(localhost|127.0.0.1)(/|$)");
private DevRefererAction() {
super(999, PATTERN);
}
}
测试环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.wsk.gateway.config;
import org.springframework.stereotype.Service;
import java.util.regex.Pattern;
/**
* @author wsk1103
* @date 2019/6/24
* @description 描述
*/
@Service
public class TestRefererAction extends AbstractRefererAction {
private static final Pattern PATTERN = Pattern.compile("^https?://(test.wsk1103.com|192.168.1.1)(/|$)");
private TestRefererAction() {
super(100, PATTERN);
}
}
生产环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.wsk.gateway.config;
import org.springframework.stereotype.Service;
import java.util.regex.Pattern;
/**
* @author wsk1103
* @date 2019/6/24
* @description 描述
*/
@Service
public class RepRefererAction extends AbstractRefererAction {
private static final Pattern PATTERN = Pattern.compile("^https?://(wsk1103.com|192.168.2.1)(/|$)");
private RepRefererAction() {
super(-999, PATTERN);
}
}
spring 全局bean管理
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
package com.wsk.gateway.config;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* @author wsk1103
* @date 2019/6/24
* @description spring 全局bean管理
*/
public class SpringContext implements ApplicationContextAware {
/**
* Spring应用上下文环境
*/
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContext.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
虽然代码看起来是增多了,但是增强了扩展性。后续的开发不需要修改原来的代码,只需要实现相应的接口并且定义正则表达式即可。
还有一种更方便的实现,使用配置中心,例如携程的 Apollo 等等,然后将所有正则表达式发布到服务器,并且解析实现。
java中的应用
java.util.logging.Logger#log()
Apache Commons Chain
javax.servlet.Filter#doFilter()
最后
- spring 的过滤器 filter 就是使用了责任链模式- ApplicationFilterChain
参考:https://github.com/iluwatar/java-design-patterns/tree/master/chain