在之前的文章中,曾经给大家介绍过策略模式:【设计模式】策略模式,在该篇文章中,我们曾很清楚的说到,策略模式主要解决的问题是:在有多种算法相似的情况下,解决使用 if...else 所带来的复杂和难以维护。策略模式使我们的实现更加符合开闭原则:面向扩展开发,面向修改关闭。在后来的学习和工作的多次应用中,有了更加深刻的认识。再次总结此篇文章赘述下。
此篇文章,将以创建订单为例,创建不同类型的订单,不同类型的订单不同的处理逻辑。
类图:
代码:
抽象策略(Strategy)角色:抽象策略角色由抽象类或接口来承担,它给出具体策略角色需要实现的接口;
package strategyTest;
import strategyTest.dto.BaseRequestDTO;
/**
* 类 名 称:CreateStrategy
* 类 描 述:策略抽象类
* 创建时间:2023/2/20 3:46 下午
* 创 建 人:admin
*/
public abstract class AbstractOrderStrategy {
public abstract Boolean isPracticable(BaseRequestDTO dto);
// 业务执行总流程
public ResponseResult process(BaseRequestDTO dto) {
ResponseResult paramsValidate = validateParams(dto);
if (!paramsValidate.isSuccess()) {
return paramsValidate;
}
ResponseResult ruleValidate = validateRules(dto);
if (!ruleValidate.isSuccess()) {
return ruleValidate;
}
ResponseResult processResult = doProcess(dto);
if(processResult.isSuccess()){
postProcessAction(dto);
}
return processResult;
}
// 参数校验
protected abstract ResponseResult validateParams(BaseRequestDTO dto);
// 业务规则校验
protected abstract ResponseResult validateRules(BaseRequestDTO dto);
// 核心实现
protected abstract ResponseResult doProcess(BaseRequestDTO dto);
// 后置处理(异步操作等)
protected abstract void postProcessAction(BaseRequestDTO dto);
}
2.具体策略(ConcreteStrategy)角色:实现封装了具体的算法或行为;
顺风车类型处理:
package strategyTest.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import strategyTest.AbstractOrderStrategy;
import strategyTest.ResponseResult;
import strategyTest.dto.BaseRequestDTO;
import strategyTest.dto.SfcRequestDTO;
/**
* 类 名 称:SfcImpl
* 类 描 述:TODO
* 创建时间:2023/2/20 3:58 下午
* 创 建 人:admin
*/
@Service
public class SfcImpl extends AbstractOrderStrategy {
private Logger logger = LoggerFactory.getLogger(SfcImpl.class);
@Override
public Boolean isPracticable(BaseRequestDTO dto) {
if(dto.getType() == 1){
return true;
}
return false;
}
@Override
protected ResponseResult validateParams(BaseRequestDTO dto) {
SfcRequestDTO sfcRequestDTO = (SfcRequestDTO) dto;
if (sfcRequestDTO.getIsShare() == null || sfcRequestDTO.getIsShare() == 0) return ResponseResult.ERROR;
logger.info("sfc validateParams success");
return ResponseResult.SUCCESS;
}
@Override
protected ResponseResult validateRules(BaseRequestDTO dto) {
// 无校验
return ResponseResult.SUCCESS;
}
@Override
protected ResponseResult doProcess(BaseRequestDTO dto) {
logger.info("sfc doProcess success");
return ResponseResult.SUCCESS;
}
@Override
protected void postProcessAction(BaseRequestDTO dto) {
logger.info("sfc 异步执行...");
}
}
巴士订单处理:
package strategyTest.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import strategyTest.AbstractOrderStrategy;
import strategyTest.ResponseResult;
import strategyTest.dto.BaseRequestDTO;
import strategyTest.dto.BusRequestDTO;
/**
* 类 名 称:BusImpl
* 类 描 述:TODO
* 创建时间:2023/2/20 3:58 下午
* 创 建 人:admin
*/
@Service
public class BusImpl extends AbstractOrderStrategy {
private Logger logger = LoggerFactory.getLogger(BusImpl.class);
@Override
public Boolean isPracticable(BaseRequestDTO dto) {
if (dto.getType() == 2) {
return true;
}
return false;
}
@Override
protected ResponseResult validateParams(BaseRequestDTO dto) {
BusRequestDTO busRequestDTO = (BusRequestDTO) dto;
if (busRequestDTO.getRouteId() == null || busRequestDTO.getRouteId() == 0) return ResponseResult.ERROR;
logger.info("bus validateParams success");
return ResponseResult.SUCCESS;
}
@Override
protected ResponseResult validateRules(BaseRequestDTO dto) {
BusRequestDTO busRequestDTO = (BusRequestDTO) dto;
if (busRequestDTO.getSeats() != null && busRequestDTO.getSeats() > 30) return ResponseResult.ERROR;
logger.info("bus validateRules success");
return ResponseResult.SUCCESS;
}
@Override
protected ResponseResult doProcess(BaseRequestDTO dto) {
logger.info("bus doProcess success");
return ResponseResult.SUCCESS;
}
@Override
protected void postProcessAction(BaseRequestDTO dto) {
logger.info("bus 异步执行...");
}
}
3.上下文(Context)角色:持有抽象策略类的引用。(引用,执行策略)
package strategyTest;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import strategyTest.dto.BaseRequestDTO;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 类 名 称:StrategyContext
* 类 描 述:策略执行上下文
* 创建时间:2023/2/20 3:47 下午
* 创 建 人:admin
*/
@Component
public class StrategyContext{
// 策略管理类
@Autowired
private List<AbstractOrderStrategy> strategies = new ArrayList<>();
// 获取策略类
public AbstractOrderStrategy getStrategy(BaseRequestDTO dto) {
for (AbstractOrderStrategy strategy : strategies) {
if (strategy.isPracticable(dto)) {
return strategy;
}
}
return null;
}
}
调用方client:
可以在一个controller,也可以写在多个controller
package strategyTest.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import strategyTest.AbstractOrderStrategy;
import strategyTest.ResponseResult;
import strategyTest.StrategyContext;
import strategyTest.dto.BaseRequestDTO;
import strategyTest.dto.BusRequestDTO;
import strategyTest.dto.SfcRequestDTO;
/**
* 类 名 称:客户端请:可以充当controller或者业务调用上游
* 类 描 述:TODO
* 创建时间:2023/2/20 3:58 下午
* 创 建 人:admin
*/
@RestController
public class ClientTest {
@Autowired
private StrategyContext strategyContext;
@RequestMapping("/createSfc")
public ResponseResult createSfc(SfcRequestDTO sfcRequestDTO){
if(sfcRequestDTO == null){
return ResponseResult.ERROR;
}
return this.createOrder(sfcRequestDTO);
}
@RequestMapping("/createBus")
public ResponseResult createBus(BusRequestDTO busRequestDTO){
if(busRequestDTO == null){
return ResponseResult.ERROR;
}
return this.createOrder(busRequestDTO);
}
private ResponseResult createOrder(BaseRequestDTO baseRequestDTO) {
AbstractOrderStrategy strategy = strategyContext.getStrategy(baseRequestDTO);
if(strategy == null){
return ResponseResult.ERROR;
}
return strategy.process(baseRequestDTO);
}
}
我们看下运行结果:
sfc运行结果:
bus运行结果:
是不是非常简洁明了!!!
我们看完代码,再结合类图看下,此时如果我们想新增一种类型的创建订单流程,比如创建一笔代驾订单,需要如何做? 我们只需要做两步:
1、新增一个代驾的实现类DjImpl,继承StrategyContext类;
2、在Controller中,新增一个方法(如果不同类型的订单有不在同一个controller,可将createOrder方法抽象到父类controller,此时新增一个子类controller继承父类和父类的方法即可);
其他地方无需改动。。。其他类型的创建订单流程完全无改动,无影响。下单流程解耦,测试范围极大的降低。
想想我们在项目中,相似的场景写过多少if else。每次添加新的实现,是否影响到原有功能。结合这篇文章,希望能对读者朋友有所帮助。
总结:
策略模式帮助我们把相似的算法抽象,使用上下文管理抽象类的具体实现,每次新增算法时,只需新增具体实现。使我们的代码轻松解耦,符合开闭原则,极大的缩小了影响范围。是成为一名优秀工程师的必修课!