策略模式
了解
当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。
如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。
策略模式可以用来消除if…else…
应用
根据不同的场景实现不同的策略
分析
代码
实现接口注入
策略接口:
提供support方法用于检查是否支持该策略,select方法用于被重写实现不同的策略逻辑
public interface TurbinesStrategy {
boolean support(String code);
/**
* 策略的对外接口,所有具体策略类都需实现该接口
* @return
*/
List<? extends ReuseVo> select(WindTurbinesDataDO windTurbinesDataDO);
}
具体的策略实现对应的接口:
@Component
public class SelectPower implements TurbinesStrategy {
@Autowired
private WindTurbinesDataMapper windTurbinesDataMapper;
@Override
public boolean support(String code) {
return "turbinesPower".equals(code);
}
/**
* 表示查询功率的具体策略,这是第一种策略
*
* @return
*/
@Override
public List<TurbinesPower> select(WindTurbinesDataDO windTurbinesDataDO) {
return windTurbinesDataMapper.selectTurbinesPowerList(windTurbinesDataDO);
}
}
@Component
public class SelectTurbinesSpeed implements TurbinesStrategy {
@Autowired
private WindTurbinesDataMapper windTurbinesDataMapper;
@Override
public boolean support(String code) {
return "TurbinesSpeed".equals(code);
}
@Override// 第二种策略
public List<TurbinesSpeed> select(WindTurbinesDataDO windTurbinesDataDO) {
return windTurbinesDataMapper.selectTurbinesSpeedList(windTurbinesDataDO);
}
}
具体使用:
@RestController
@RequestMapping("/windTurbines")
public class WindTurbinesDataController extends BaseController implements ApplicationContextAware, InitializingBean {
private List<TurbinesStrategy> strategieList = new ArrayList<>();
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;// 将Spring容器注入进来,需要实现ApplicationContextAware接口
}
@Override
public void afterPropertiesSet() {// 实现InitializingBean接口,根据bean类型拿到BeanMap,转为list
Map<String, TurbinesStrategy> beansOfType = context.getBeansOfType(TurbinesStrategy.class);
System.out.println(beansOfType);
beansOfType.forEach((key,value)->strategieList.add(value));
}
/**
* 查询风电机组列表
*/
@PreAuthorize("@ss.hasPermi('dataManagement:windTurbines:list')")
@GetMapping("/list")
public TableDataInfo list(WindTurbinesDataDO windTurbinesDataDO)
{
String code = windTurbinesDataDO.getCode();// 根据前端传递的code码调用相应的策略
List<? extends ReuseVo> select = null;
for (TurbinesStrategy turbinesStrategy : strategieList) {// 遍历策略list找到该策略,然后调用实现
if (turbinesStrategy.support(code)){
select = turbinesStrategy.select(windTurbinesDataDO);
}
}
return getDataTable(select);
}
}
这里其实还可以直接使用@Autowired直接将策略bean注入进来,而不必要实现接口再拿,这样support接口也可以省略。
在使用@Component将策略注入Spring容器的时候,指定存入容器bean的名字(不指定默认为该类的类名首字母小写),在使用时直接使用@Autowired注解注入map或者list,
map可以直接get,list仍需要遍历寻找。
如果不指定注入bean的名字,需要前端传递的code码与策略类名首字母小写对应,指定bean名可以传递一般化的参数,与指定的名字对应即可。
使用@Autowired注入
@RestController
@RequestMapping("/windTurbines")
public class WindTurbinesDataController extends BaseController {
@Autowired
private Map<String, TurbinesStrategy> strategieMap = new HashMap<>();
/**
* 查询风电机组列表
*/
@PreAuthorize("@ss.hasPermi('dataManagement:windTurbines:list')")
@GetMapping("/list")
public TableDataInfo list(WindTurbinesDataDO windTurbinesDataDO) {
String code = windTurbinesDataDO.getCode();
List<? extends ReuseVo> select = null;
TurbinesStrategy turbinesStrategy = strategieMap.get(code);
select = turbinesStrategy.select(windTurbinesDataDO);
return getDataTable(select);
}
}
使用map效果图:key为我指定的bean名,value是我注入的类(具体的策略实现)
效果
- 减少了前端接口调用次数
- 避免了查重复的数据,减少了不必要的数据查询,减少了后端接口的数量
- 减轻数据库压力提高效率
- 并且复用接口,提高了可扩展性
扩展
策略如果没有找到呢?如何实现默认策略?由于我上面的业务不需要用到默认策略实现,所以在这里进行扩展。
抽象策略(Strategy)类
具体策略(Concrete Strategy)类
环境(Context)类
使用策略模式的第三个角色—环境(Context)类,上面的例子只使用到前两个角色,那么使用第三个角色的策略模式怎么实现呢?
额。。。其实控制层就是环境类,策略对象(本身/map/List)是它的成员变量
那么:
还可以使用枚举去实现具体的策略,这样做可以使用一个文件即做到实现不同的策略
public enum StrategyEnum {
A {
@Override
public List<? extends ReuseVo> select(WindMeasurementTowerDataDO windMeasurementTowerDataDO) {
return null;
}
},
B {
@Override
public List<? extends ReuseVo> select(WindMeasurementTowerDataDO windMeasurementTowerDataDO) {
return null;
}
};
List<? extends ReuseVo> select(WindMeasurementTowerDataDO windMeasurementTowerDataDO) {
return null;
}
}
牵扯出另一个问题:
[怎么往枚举类里注入Bean](# 怎么往枚举类里注入Bean)
使用枚举类使用效果成功,枚举类的valueof()方法通过前端的code码获得对应的枚举类型,执行被重写的具体策略。
存疑
mapper接口能使用泛型协变逆变吗???如何实现
List<? extends ReuseVo> selectWindMeasurementTowerDataListReuse(WindMeasurementTowerDataDO windMeasurementTowerDataDO);
怎么往枚举类里注入Bean
如果在枚举类上加@Component注解会导致如下报错:
Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException
可以得知枚举类是无法直接交给Spring容器管理的。
究其原因,是因为Spring没有找到构造方法,那么我给枚举添加构造方法之后会怎么样呢?
还是创建Bean失败,不能找到构造方法,因为枚举类的构造方法不是public的,并且也不能用public修饰。
解决:
使用内部类的方式,借由内部类暴露所需要的组件
public enum StrategyEnum {
A {
@Override
public List<? extends ReuseVo> select(WindMeasurementTowerDataDO windMeasurementTowerDataDO) {
return MyBeanAContainer.getMyBeanA().selectWindMeasurementTowerDataSpeedList(windMeasurementTowerDataDO);
}
},
B {
@Override
public List<? extends ReuseVo> select(WindMeasurementTowerDataDO windMeasurementTowerDataDO) {
return MyBeanAContainer.getMyBeanA().selectWindMeasurementTowerDataSurfacePressureList(windMeasurementTowerDataDO);
}
};
public abstract List<? extends ReuseVo> select(WindMeasurementTowerDataDO windMeasurementTowerDataDO);
@PostConstruct
public void init(){
}
@Component
static class MyBeanAContainer {
@Autowired
WindMeasurementTowerDataMapper windMeasurementTowerDataMapper;
private static WindMeasurementTowerDataMapper mapper;
@PostConstruct// 静态属性无法直接注入,我们借由 @PostConstruct 初始化回调为它赋值,该回调方法在所有 bean 注册完成后,因此没有问题
public void init() {// 该方法也是解决 静态属性注入 最常用的方法
mapper = windMeasurementTowerDataMapper;
}
static WindMeasurementTowerDataMapper getMyBeanA() {
return mapper;
}
}
}
标签:code,return,策略,List,模式,public,select
From: https://www.cnblogs.com/code-jia/p/18091164