1. 什么是策略模式:
策略模式就是一种行为可能会因为不同的逻辑造成多个算法。比如人吃饭,美国人吃饭用刀叉,中国吃饭用筷子。都是吃饭的行为但是使用的工具(算法)不一样。而策略行为就是把这写工具封装为对象,不同的人会动态的调用不同的对象,来实现吃饭这一行为。
2. 策略模式组成:
- 环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
- 抽象策略类(Strategy):定义所有支持的算法的公共接口。 Context使用这个接口来调用某ConcreteStrategy定义的算法。
- 具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。
3. 策略模式UML图
4. 示例
就按照上面提到的例子,按照策略模式UML图按照策略模式实现人吃饭这件事情。
4.1 创建Strategy抽象策略类
public interface Strategy {
/**
* 人类怎么吃饭的
*/
public void eat();
}
4.2 创建环境类
public class Context {
private Strategy strategy;
public Context(){
}
public Context(Strategy strategy){
this.strategy = strategy;
}
public void setStrategy(Strategy strategy){
this.strategy = strategy;
}
public void eat(){
strategy.eat();
}
}
上面环境类我根据我是实现了两种方式传入策略类,可以根据自己不同的需求就行灵活应用给
4.3 创建实现
/**
* 美国人 吃饭
*/
@Component
public class StrategyImplOne implements Strategy{
@Override
public void eat() {
System.out.println("美国人用刀叉吃饭");
}
}
/**
* 中国人 吃饭
*/
@Component
public class StrategyImplTwo implements Strategy{
@Override
public void eat() {
System.out.println("中国人用筷子吃饭");
}
}
4.4 测试
public class MainTest {
public static void test(){
Context context = new Context();
context.setStrategy(new StrategyImplOne());
context.eat();
context.setStrategy(new StrategyImplTwo());
context.eat();
}
public static void main(String[] args) {
test();
}
}
当然了这是简单的场景,在复杂的场景中我们不想经过判断他是美国人还是中国人来创建对应的实现,设置到环境类中,我们可以给每个实现设置一个属性标识,来标识他是中国人还是美国人,然后根据标识通过反射创建对应的实现对象,再设置到环境类中。下面我们看结果。
首先我们加入一个枚举类(enum) 用来存储我们实现的策略类
public enum ManType {
AMERICAN(1,"American",StrategyImplOne.class),
CHINESE(1,"Chinese",StrategyImplTwo.class)
;
private Integer code;
private String type;
private Class classModel;
// 定义一个Map 开始的时候就把这信息存储到这里面
private static HashMap<String,Class> map = new HashMap<>(16);
static {
for ( ManType manType:ManType.values()) {
map.put(manType.getType(),manType.getClassModel());
}
}
ManType(Integer code, String type, Class classModel) {
this.code = code;
this.type = type;
this.classModel = classModel;
}
public Integer getCode() {
return code;
}
public String getType() {
return type;
}
public Class getClassModel() {
return classModel;
}
// 这个方法根据type获取指定的类 通过反射来 创建对象
public static Class getManType(String type){
return map.get(type);
}
}
创建好枚举类后,我们的测试方法就变为下面的这个样子。写的有点粗糙,大家见谅。
public class MainTest {
public void test(String manType){
Context context = new Context();
try {
Strategy strategy = (Strategy) (ManType.getManType(manType).newInstance());
context.setStrategy(strategy);
context.eat();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new MainTest().test("American");
}
}
其实也可以不用反射,如果项目是使用的是SpringBoot开发的话 我们可以把实现策略类在项目启动时就给通过@Component
给注册到Spring容器中,然后通过ApplicationContext 从容器中获取指定的对象。这样也是可以的。我就不一一展示了,感兴趣的话可以自己在下面试一下。
优点和缺点
优点:
1、策略模式符合开闭原则。
2、避免使用多重条件转移语句,如if…else…语句、switch 语句
3、使用策略模式可以提高算法的保密性和安全性。
缺点:
1、客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
2、代码中会产生非常多策略类,增加维护难度。
3、如果把策略类初始化时就放到Spring容器中,当策略类太多事也是一种性能消耗。