原创文章,转载请务必将下面这段话置于文章开头处(保留超链接)。
本文转发自技术世界,原文链接 http://www.jasongj.com/design_pattern/strategy/
策略模式介绍
策略模式定义
策略模式(Strategy Pattern),将各种算法封装到具体的类中,作为一个抽象策略类的子类,使得它们可以互换。客户端可以自行决定使用哪种算法。
策略模式类图
策略模式类图如下
策略模式角色划分
Strategy 策略接口或者(抽象策略类),定义策略执行接口
ConcreteStrategy 具体策略类
Context 上下文类,持有具体策略类的实例,并负责调用相关的算法
策略模式实例解析
本文代码可从作者Github下载
典型策略模式实现
策略接口,定义策略执行接口1
2
3
4
5
6
7package com.jasongj.strategy;
public interface Strategy {
void strategy(String input);
}
具体策略类,实现策略接口,提供具体算法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.jasongj.strategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
"StrategyA") .jasongj.annotation.Strategy(name=
public class ConcreteStrategyA implements Strategy {
private static final Logger LOG = LoggerFactory.getLogger(ConcreteStrategyB.class);
public void strategy(String input) {
LOG.info("Strategy A for input : {}", input);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.jasongj.strategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
"StrategyB") .jasongj.annotation.Strategy(name=
public class ConcreteStrategyB implements Strategy {
private static final Logger LOG = LoggerFactory.getLogger(ConcreteStrategyB.class);
public void strategy(String input) {
LOG.info("Strategy B for input : {}", input);
}
}
Context类,持有具体策略类的实例,负责调用具体算法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.jasongj.context;
import com.jasongj.strategy.Strategy;
public class SimpleContext {
private Strategy strategy;
public SimpleContext(Strategy strategy) {
this.strategy = strategy;
}
public void action(String input) {
strategy.strategy(input);
}
}
客户端可以实例化具体策略类,并传给Context类,通过Context统一调用具体算法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package com.jasongj.client;
import com.jasongj.context.SimpleContext;
import com.jasongj.strategy.ConcreteStrategyA;
import com.jasongj.strategy.Strategy;
public class SimpleClient {
public static void main(String[] args) {
Strategy strategy = new ConcreteStrategyA();
SimpleContext context = new SimpleContext(strategy);
context.action("Hellow, world");
}
}
使用Annotation和简单工厂模式增强策略模式
上面的实现中,客户端需要显示决定具体使用何种策略,并且一旦需要换用其它策略,需要修改客户端的代码。解决这个问题,一个比较好的方式是使用简单工厂,使得客户端都不需要知道策略类的实例化过程,甚至都不需要具体哪种策略被使用。
如《Java卡塔尔世界杯BOB体育官方APP登入(一) 简单工厂模式不简单》所述,简单工厂的实现方式比较多,可以结合《Java系列(一)Annotation(注解)》中介绍的Annotation方法。
使用Annotation和简单工厂模式的Context类如下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
63package com.jasongj.context;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jasongj.strategy.Strategy;
public class SimpleFactoryContext {
private static final Logger LOG = LoggerFactory.getLogger(SimpleFactoryContext.class);
private static Map<String, Class> allStrategies;
static {
Reflections reflections = new Reflections("com.jasongj.strategy");
Set<Class<?>> annotatedClasses =
reflections.getTypesAnnotatedWith(com.jasongj.annotation.Strategy.class);
allStrategies = new ConcurrentHashMap<String, Class>();
for (Class<?> classObject : annotatedClasses) {
com.jasongj.annotation.Strategy strategy = (com.jasongj.annotation.Strategy) classObject
.getAnnotation(com.jasongj.annotation.Strategy.class);
allStrategies.put(strategy.name(), classObject);
}
allStrategies = Collections.unmodifiableMap(allStrategies);
}
private Strategy strategy;
public SimpleFactoryContext() {
String name = null;
try {
XMLConfiguration config = new XMLConfiguration("strategy.xml");
name = config.getString("strategy.name");
LOG.info("strategy name is {}", name);
} catch (ConfigurationException ex) {
LOG.error("Parsing xml configuration file failed", ex);
}
if (allStrategies.containsKey(name)) {
LOG.info("Created strategy name is {}", name);
try {
strategy = (Strategy) allStrategies.get(name).newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
LOG.error("Instantiate Strategy failed", ex);
}
} else {
LOG.error("Specified Strategy name {} does not exist", name);
}
}
public void action(String input) {
strategy.strategy(input);
}
}
从上面的实现可以看出,虽然并没有单独创建一个简单工厂类,但它已经融入了简单工厂模式的设计思想和实现方法。
客户端调用方式如下1
2
3
4
5
6
7
8
9
10
11
12package com.jasongj.client;
import com.jasongj.context.SimpleFactoryContext;
public class SimpleFactoryClient {
public static void main(String[] args) {
SimpleFactoryContext context = new SimpleFactoryContext();
context.action("Hellow, world");
}
}
从上面代码可以看出,引入简单工厂模式后,客户端不再需要直接实例化具体的策略类,也不需要判断应该使用何种策略,可以方便应对策略的切换。