策略模式:
介绍
定义一系列算法,然后将每一个算法封装起来,并将它们可以互相替换。也就是将一系列算法封装到一系列策略类里面。策略模式是一种对象行为型模式。详细介绍可参阅:[策略模式Strategy](https://blog.csdn.net/hguisu/article/details/7558249/)
适用性
用于处理相同场景下不同对象(类型)不同而具体处理业务逻辑有差异。我们往往会使用if...else或者switch-case语句,会造成代码可读性变差。所以将这些处理方式,组合构成算法策略族,它们的共性,体现在策略接口行为上,消除一些if else条件语句。
策略模式的角色:
环境(Context)角色:持有一个Strategy的引用。
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的具体算法或行为。
注解实现:
业务场景:
假设有这么一个场景,订单可以根据产品类型做活动比如 打折,每个产品的
打折情况不一样(实际场景应该根据配置处理即可,此处仅作为一种思路)。比如:电脑打6折,手机打8.2折,电视7.6折等。
定义枚举:
定义一个电子产品类型枚举:ElectronicEnum
/**
* @author xuan
* @version 1.0.0
* @Description 电子产品类型
* @createTime 2022年01月02日 14:46
*/
public enum ElectronicEnum {
CELLPHONE("001","手机"),
COMPUTER("002","电脑"),
TV("003","电视");
private String type;
private String desc;
public String getType() {
return type;
}
public String getDesc() {
return desc;
}
/**
*
* @param type
* @param desc
*/
ElectronicEnum(String type, String desc) {
this.type = type;
this.desc = desc;
}
}
自定义注解
我们写一个注解 ElectronicType,该注解用在实现类上,表名该类代表的是何种电子产品类型
/**
* @author xuan
* @version 1.0.0
* @Description 电子产品类型注解
* @createTime 2022年01月02日 14:45
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ElectronicType {
/**
* 策略类型
* @return
*/
ElectronicEnum value();
}
定义策略接口:
定义电子产品业务接口:ElectronicProductService
/**
* @author xuan
* @version 1.0.0
* @Description 电子产品业务
* @createTime 2022年01月02日 14:56
*/
public interface ElectronicProductService {
/**
* 打折
* @param params 业务参数
* @return
*/
String discount(String params);
/**
* 满减活动
* @param orderVo 业务参数
* @return
*/
OrderVo fullReduction(OrderVo orderVo);
}
业务实现
我们根据3中产品类型编写3个实现类,电脑、电视、手机。
关键点在于实现类上添加上我们自定义的注解
@ElectronicType(ElectronicEnum.COMPUTER)
其中注解元素值为各个类型枚举,如以下:
1.电脑类型实现类:ComputerServiceImpl
/**
* @author xuan
* @version 1.0.0
* @Description 电脑
* @createTime 2022年01月02日 14:58
*/
@Slf4j
@Component
@ElectronicType(ElectronicEnum.COMPUTER)
public class ComputerServiceImpl implements ElectronicProductService {
@Override
public String discount(String params) {
// 具体的业务逻辑
return "电脑 打6折";
}
@Override
public OrderVo fullReduction(OrderVo orderVo) {
OrderVo orderVo1 = new OrderVo();
BeanUtils.copyProperties(orderVo,orderVo1);
log.info("电视:满2200减25");
// 赋值 模拟业务 显示
orderVo1.setProductName("电脑");
orderVo1.setRemark("满2200减25");
orderVo1.setPayAmt(orderVo.getOrderAmt());
orderVo1.setDiscountAmt(new BigDecimal("0"));
if (orderVo.getOrderAmt().compareTo(new BigDecimal("2200"))==1){
BigDecimal discountAmt=new BigDecimal("25");
orderVo1.setDiscountAmt(discountAmt);
orderVo1.setPayAmt(orderVo.getOrderAmt().subtract(discountAmt));
}
return orderVo1;
}
}
2.手机类型实现类:MobilePhoneServiceImpl
/**
* @author xuan
* @version 1.0.0
* @Description 手机
* @createTime 2022年01月02日 14:59
*/
@Slf4j
@Component
@ElectronicType(ElectronicEnum.CELLPHONE)
public class MobilePhoneServiceImpl implements ElectronicProductService {
@Override
public String discount(String params) {
// 具体的业务逻辑
return "手机打8.2折";
}
@Override
public OrderVo fullReduction(OrderVo orderVo) {
OrderVo orderVo1 = new OrderVo();
BeanUtils.copyProperties(orderVo,orderVo1);
log.info("电视:满500减15");
// 赋值 模拟业务 显示
orderVo1.setProductName("手机");
orderVo1.setRemark("满500减15");
orderVo1.setPayAmt(orderVo.getOrderAmt());
orderVo1.setDiscountAmt(new BigDecimal("0"));
if (orderVo.getOrderAmt().compareTo(new BigDecimal("500"))==1){
BigDecimal discountAmt=new BigDecimal("15");
orderVo1.setDiscountAmt(discountAmt);
orderVo1.setPayAmt(orderVo.getOrderAmt().subtract(discountAmt));
}
return orderVo1;
}
}
3.电视类型实现类:MobilePhoneServiceImpl
/**
* @author xuan
* @version 1.0.0
* @Description 电视
* @createTime 2022年01月02日 15:00
*/
@Slf4j
@Component
@ElectronicType(ElectronicEnum.TV)
public class TVServiceImpl implements ElectronicProductService {
@Override
public String discount(String params) {
// 业务...
return "电视7.6折";
}
/**
* 满减
* @param orderVo 业务参数
* @return
*/
@Override
public OrderVo fullReduction(OrderVo orderVo) {
OrderVo orderVo1 = new OrderVo();
BeanUtils.copyProperties(orderVo,orderVo1);
log.info("电视:满满1000减20");
// 赋值 模拟业务 显示
orderVo1.setProductName("电视");
orderVo1.setRemark("满满1000减20");
orderVo1.setPayAmt(orderVo.getOrderAmt());
orderVo1.setDiscountAmt(new BigDecimal("0"));
if (orderVo.getOrderAmt().compareTo(new BigDecimal("1000"))==1){
BigDecimal discountAmt=new BigDecimal("20");
orderVo1.setDiscountAmt(discountAmt);
orderVo1.setPayAmt(orderVo.getOrderAmt().subtract(discountAmt));
}
return orderVo1;
}
}
策略接口实现类工厂
启动时加载电子产品业务所有因子,将所有的实现类根据类型封装到enumElectronicProductServiceMap变量中,
根据产品类型获取对应的实现类。
1.实现sping bean上下文
implements ApplicationContextAware
2.定义变量
private static Map<ElectronicEnum, ElectronicProductService> enumElectronicProductServiceMap;
3.自动加载所有因子组实现类
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 获取 ElectronicProductService 接口的所有实现类
Map<String,ElectronicProductService> electronicProductServiceMap=applicationContext.getBeansOfType(ElectronicProductService.class);
enumElectronicProductServiceMap = new HashMap<>();
// 将实现类放入map中,key为实现类对应的枚举,value为实现类
for (ElectronicProductService item: electronicProductServiceMap.values()) {
ElectronicType annotation = item.getClass().getAnnotation(ElectronicType.class);
if (null != annotation ){
enumElectronicProductServiceMap.put(annotation.value(),item);
}
}
}
4.定义实现类方法
/**
* 获取实现类
* @param type
* @return electronicProductService
* @throws BusException
*/
public static ElectronicProductService getElectronicProductServiceImpl(String type) throws BusException {
return enumElectronicProductServiceMap.get(getElectronicEnum(type));
}
public static ElectronicEnum getElectronicEnum(String type){
for (ElectronicEnum item: ElectronicEnum.values()) {
if (item.getType().equals(type)){
return item;
}
}
throw new BusException("401","服务不存在!");
}
5:完整代码
/**
* @author xuan
* @version 1.0.0
* @Description ElectronicProductService 接口实现类
* @createTime 2022年01月21日 10:51
*/
@Component
@Slf4j
public class ElectronicProductServiceFactory implements ApplicationContextAware {
/** 存所有 ElectronicProductService 实现类 */
private static Map<ElectronicEnum, ElectronicProductService> enumElectronicProductServiceMap;
/**
* 自动加载所有因子组实现类
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 获取 ElectronicProductService 接口的所有实现类
Map<String,ElectronicProductService> electronicProductServiceMap=applicationContext.getBeansOfType(ElectronicProductService.class);
enumElectronicProductServiceMap = new HashMap<>();
// 将实现类放入map中,key为实现类对应的枚举,value为实现类
for (ElectronicProductService item: electronicProductServiceMap.values()) {
ElectronicType annotation = item.getClass().getAnnotation(ElectronicType.class);
if (null != annotation ){
enumElectronicProductServiceMap.put(annotation.value(),item);
}
}
}
/**
* 获取实现类
* @param type
* @return electronicProductService
* @throws BusException
*/
public static ElectronicProductService getElectronicProductServiceImpl(String type) throws BusException {
return enumElectronicProductServiceMap.get(getElectronicEnum(type));
}
public static ElectronicEnum getElectronicEnum(String type){
for (ElectronicEnum item: ElectronicEnum.values()) {
if (item.getType().equals(type)){
return item;
}
}
throw new BusException("401","服务不存在!");
}
}
电子商品业务策略
该类(ElectronicProductServiceStrategy)呢也是去实现
ElectronicProductService 接口,该实现类和前面3个实现类不同在于,
该实现方法不写具体业务逻辑而是根据 产品类型去调用前面3种实现类,
从而达到每个实现类只需关注自己业务逻辑,代码解耦;
此类上不写我们自定义的注解而是加上另一个注解@Primary;
这样在调用ElectronicProductService 接口时自动调用该实现类。
1:ElectronicProductServiceStrategy实现类
/**
* @author xuan
* @version 1.0.0
* @Description 电子商品业务策略
* @createTime 2022年01月02日 15:06
*/
@Slf4j
@Primary
@Component
public class ElectronicProductServiceStrategy implements ElectronicProductService {
/**
* 打折
* @param type
* @return
*/
@Override
public String discount(String type) {
ElectronicProductService electronicProductService=ElectronicProductServiceFactory.getElectronicProductServiceImpl(type);
return electronicProductService.discount("这是参数");
}
/**
* 满减
* @param orderVo 业务参数
* @return
*/
@Override
public OrderVo fullReduction(OrderVo orderVo) {
ElectronicProductService electronicProductService=ElectronicProductServiceFactory.getElectronicProductServiceImpl(orderVo.getType());
return electronicProductService.fullReduction(orderVo);
}
}
2.调用
构造注入ElectronicProductService ,此处实际首先调用的实现类是:
ElectronicProductServiceStrategy ,因为我们加了注解@Primary
private final ElectronicProductService electronicProductService ;
public ElectronicProductController( ElectronicProductService electronicProductService){
this.electronicProductService=electronicProductService;
}
3.完整类
/**
* @author xuan
* @version 1.0.0
* @Description 干掉 IF ELSE的 第一种模式
* @createTime 2022年01月02日 15:01
*/
@Slf4j
@RestController
@RequestMapping("/order")
public class ElectronicProductController {
// @Resource
// private ElectronicProductService electronicProductService ;
private final ElectronicProductService electronicProductService ;
public ElectronicProductController( ElectronicProductService electronicProductService){
this.electronicProductService=electronicProductService;
}
/**
* 打折
* @param type
* @return
*/
@ResponseBody
@RequestMapping("/discount/{type}")
public Object discount(@PathVariable String type){
return electronicProductService.discount(type);
}
/**
* 优惠
* @param type
* @return
*/
@ResponseBody
@RequestMapping("/fullReduction/{type}")
public Object fullReduction(@PathVariable String type){
OrderVo orderVo = new OrderVo();
orderVo.setType(type);
orderVo.setOrderAmt(getBigDecimalF2(5000));
orderVo.setOrderId(UUID.randomUUID().toString().toLowerCase().replace("-",""));
OrderVo reOrderVo = electronicProductService.fullReduction(orderVo);
return Result.sucess(reOrderVo);
}
public BigDecimal getBigDecimalF2(int max){
Double rand=Math.random()*max;
DecimalFormat decimalFormat = new DecimalFormat("0.00");
String randDec=decimalFormat.format(rand);
return new BigDecimal(randDec);
}
}
测试
执行main方法,浏览器输入
http://127.0.0.1:1800/order/discount/001 输出:手机打8.2折
http://127.0.0.1:1800/order/discount/002 输出:电脑 打6折
http://127.0.0.1:1800/order/discount/003 输出:电视7.6折
符合我们的预期,至此消除了if-esle(switch-case)语句块
项目演示demo源码
github: https://github.com/xuanzhiyigit/ifelse
gitee: https://gitee.com/xuanzhiyi/ifelse