Strategy pattern

概念

在计算机编程中,策略模式是一种行为 软件设计模式,可以在运行时选择算法。代码不是直接实现单个算法,而是接收关于在一系列算法中使用哪些算法的运行时指令。

举例

策略模式可以说是一种选择,它会根据调用者的属性去调用对应的算法,动态地改变对象的行为,用户可以调正对应的策略达到想要的目的。下面会举一个策略模式的Demo场景,同时将分析 Spring 中的策略模式。

组成

Context:一般提供setStrategy(strategy),用于设置对应的策略,同时起到封装的作用,屏蔽直接访问实际策略。

Srategy:strategy公共接口,规定了策略的属性,方法

ConcreteStrategy:实际策略类,实现了接口,根据具体策略实现对应的算法。

举例

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
/**
* Created by Heiku on 2018/7/31
*
* Strategy pattern
*/

// 策略接口 购买手机的客户
interface Client{
void introduct();
}

// 果粉
class AppleFans implements Client{
@Override
public void introduct() {
System.out.println("向果粉介绍苹果新产品");
}
}

// 游戏玩家
class Gamer implements Client{

@Override
public void introduct() {
System.out.println("向手机玩家介绍 黑鲨等游戏手机");
}
}

// 学生党
class Student implements Client{
@Override
public void introduct() {
System.out.println("向学生党介绍最新发布的手机");
}
}

// 手机城
public class PhoneShop {
private Client client = null;

public void setClient(Client client) {
this.client = client;
}

public void introduct(){
client.introduct();
}


public static void main(String[] args) {
PhoneShop shop = new PhoneShop();

// 果粉进店
System.out.println("果粉进店");
shop.setClient(new AppleFans());
shop.introduct();

// 游戏发烧友进店
System.out.println("游戏发烧友进店");
shop.setClient(new Gamer());
shop.introduct();


// 学生党进店
System.out.println("学生党进店");
shop.setClient(new Student());
shop.introduct();
}
}

// 结果
果粉进店
向果粉介绍苹果新产品
游戏发烧友进店
向手机玩家介绍 黑鲨等游戏手机
学生党进店
向学生党介绍最新发布的手机

Spring中的实际应用

在 SpringMVC中,当启动服务器时,request进来时,DispatcherSerlvet作为请求入口,会依次调用HttpServletBean -> FrameworkServlet -> DispatcherServlet,完成容器的初始化,注意几个servlet是向上的继承关系.

在FrameworkServlet中会调用 initWebApplicationContext()-> onRefresh()

1
2
3
4
5
6
protected WebApplicationContext initWebApplicationContext() {
...
if (!this.refreshEventReceived) {
this.onRefresh(wac);
}
...

接着,在DispatcherServlet时,会进行bean的初始化,调用onRefresh() -> initStrategies(),进行九大组件的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 刷新容器时,将策略更新
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}

// 进行初始化策略
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}

根据context上下文,获取对应策略

1
2
3
4
5
6
7
8
9
// 获取对应策略
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
List<T> strategies = this.getDefaultStrategies(context, strategyInterface);
if (strategies.size() != 1) {
throw new BeanInitializationException("DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
} else {
return strategies.get(0);
}
}
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
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value == null) {
return new LinkedList();
} else {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList(classNames.length);
String[] var7 = classNames;
int var8 = classNames.length;

for(int var9 = 0; var9 < var8; ++var9) {
String className = var7[var9];
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = this.createDefaultStrategy(context, clazz);
strategies.add(strategy);
} catch (ClassNotFoundException var13) {
throw new BeanInitializationException("Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", var13);
} catch (LinkageError var14) {
throw new BeanInitializationException("Error loading DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]: problem with class file or dependent class", var14);
}
}

return strategies;
}
}