1、策略模式定义:
策略模式(Strategy Pattern)定义了一组策略,分别在不同类中封装起来,每种策略都可以根据当前场景相互替换,从而使策略的变化可以独立于操作者。比如我们要去某个地方,会根据距离的不同来选择不同的出行方式,这些出行方式即不同的策略。
个人理解:
 就是定义了一个策略接口,然后有多种策略实现类去实现策略接口。
2、何时使用策略模式
阿里开发规约-编程规约-控制语句-第六条 :超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现。
相信大家都见过这种代码:
if (conditionA) {
    逻辑1
} else if (conditionB) {
    逻辑2
} else if (conditionC) {
    逻辑3
} else {
    逻辑4
}
这种代码虽然写起来简单,但是很明显违反了面向对象的2个基本原则:
- 单一职责原则(一个类应该只有一个发生变化的原因):因为之后修改任何一个逻辑,当前类都会被修改
- 开闭原则(对扩展开发,对修改关闭):如果此时需要添加(删除)某个逻辑操作,那么就会修改原来的代码
尤其是当if-else 块中的代码量比较大时,后续代码的扩展和维护就会逐渐变得非常困难且容易出错,使用switch 语句也同样避免不了以上两个问题。
比较好的实践:
- if-else 不超过 2 层,块中代码 1~5 行,直接写到块中,否则封装为方法
- if-else 超过 2 层,但块中的代码不超过 3 行,尽量使用卫语句
- if-else 超过 2 层,且块中代码超过 3 行,尽量使用策略模式
3、策略模式实践
在Spring中,如何巧妙的运用策略模式。
3.1、需求背景
我们按照前面说的,我们以去某个地方为由,会根据距离的不同而选择不同的出行方式。
出行的策略(方式):
- 步行
- 出租车
- 地铁
3.2、第一步,定义策略接口
首先定义策略接口,包括两个方法:
- 获取策略类型的方法
- 处理策略逻辑的方法
public interface ActionHandler {
	/**
	*	获取策略的类型
	*/
    public String actionMethod();
	
	/**
	*	处理策略的逻辑
	*/
    public Object handler();
}
3.3、第二步,相关策略实现
这里我定义了一个枚举类,用来表示策略的类型及其含义:
public enum ActionMethodEnum {
    WALK("by_walk","步行"),
    CAR("by_car","出租车"),
    SUBWAY("by_subway","地铁"),
    CYCLE("by_cycle","自行车");
    private String method;
    private String desc;
    ActionMethodEnum(String method,String desc){
        this.method = method;
        this.desc = desc;
    }
    public String getMethod() {
        return method;
    }
    public String getDesc() {
        return desc;
    }
}
步行策略实现类:
@Component
public class ByWalkActionHandler implements ActionHandler {
    @Override
    public String actionMethod() {
        return ActionMethodEnum.WALK.getMethod();
    }
    @Override
    public Object handler() {
        System.out.println("步行出行。。");
        return ActionMethodEnum.WALK.getDesc();
    }
}
出租车策略实现类:
@Component
public class ByCarActionHandler implements ActionHandler {
    @Override
    public String actionMethod() {
        return ActionMethodEnum.CAR.getMethod();
    }
    @Override
    public Object handler() {
        System.out.println("出租车出行。。。");
        return ActionMethodEnum.CAR.getDesc();
    }
}
地铁策略实现类:
@Component
public class BySubwayActionHandler implements ActionHandler {
    @Override
    public String actionMethod() {
        return ActionMethodEnum.SUBWAY.getMethod();
    }
    @Override
    public Object handler() {
        System.out.println("地铁出行。。。");
        return ActionMethodEnum.SUBWAY.getDesc();
    }
}
3.4、建立策略的简单工厂
Tips:
 这里使用简单工厂是为了管理我们的策略实现类,将这些策略放入一个Map集合中,后续可以根据策略的类型获取对应的策略处理器。
@Component
public class ActionMethodContext implements InitializingBean, ApplicationContextAware {
    private ApplicationContext applicationContext;
    private Map<String, ActionHandler> methodMap = new HashMap<>();
	/**
	 * 将Spring容器中所有实现了策略接口的类添加到Map集合中 
	*/
    @Override
    public void afterPropertiesSet() throws Exception {
        applicationContext.getBeansOfType(ActionHandler.class).values()
                .stream().forEach(actionHandler -> methodMap.put(actionHandler.actionMethod(),actionHandler));
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    /**
     * 根据出行类型获取对应的策略方法
     * @param actionMethod
     * @return
     */
    public ActionHandler getActionMethod(String actionMethod){
        return methodMap.getOrDefault(actionMethod,new ByWalkActionHandler());
    }
}
4、使用 & 测试
创建了一个controller来简单的测试:
@RestController
public class ActionController {
    @Autowired
    ActionMethodContext actionMethodContext;
    @GetMapping("/action")
    public String doAction(String actionMethod){
        ActionHandler actionHandler = actionMethodContext.getActionMethod(actionMethod);
        String result = (String) actionHandler.handler();
        return result;
    }
}
使用postman简单的测试一下:

 
 
Factory 只负责获取 Handler,Handler 只负责处理具体的提交,Service 只负责逻辑编排,从而达到功能上的 “低耦合高内聚”。
5、扩展
如果我们需要加入一个新的策略,比如自行车出行,我们只需要添加一个新的策略实现即可:
@Component
public class ByCycleActionHandler implements ActionHandler {
    @Override
    public String actionMethod() {
        return ActionMethodEnum.CYCLE.getMethod();
    }
    @Override
    public Object handler() {
        System.out.println("自行车出行。。。");
        return ActionMethodEnum.CYCLE.getDesc();
    }
}
此时不需要修改原有的逻辑,在Spring容器重启时会自动将自行车策略类添加到我们的简单工厂类中。



















