提到设计模式,我想大家最熟悉的莫过于单例模式
了,很多人应该都能写出好几种单例模式
的实现。工作中除了单例模式之外,还有一种比较常用的设计模式就是策略模式
了。
正好前段時間的需求用到了策略模式,进过多次调整改进,自认为实现了一种比较优雅的策略模式
,现在分享出来,希望可以帮助到你。
作为一篇完整的文章,我们还是有必要介绍下什么是策略模式,然后会通过一个完整案例,实现一个策略模式的具体编码。
什么是策略模式
?
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。 ----- 百度百科
大白话说就是,一个接口有多个实现类,在具体调用方法的时候,依据所需选择一个具体实现类去调用。
策略模式
的组成
策略模式的组成组要分为3个部分
策略模式
实现案例
下面就以一个商场促销的例子来实现一个策略模式
。
首先定义接口
package com.info.examples.strategy;
import java.math.BigDecimal;
public interface PromoteSalesService {
/**
* 获取商品促销价
*
* @param originalPrice 原价
* @return 商品的促销价
*/
BigDecimal getPromotionPrice(BigDecimal originalPrice);
}
这里给到两种促销方案,一个是满199减50,一种是满100打9折,199以上打85折。
首先我们定义一个促销方式的枚举,方便后期使用
package com.info.examples.strategy;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 支持的促销方式枚举
*/
@AllArgsConstructor
@Getter
public enum PromoteTypeEnum {
// 打折
DISCOUNT("打折"),
// 满减
FULL_REDUCTION("满减");
private String desc;
}
相应的,我们会在接口类PromoteSalesService.java
添加一个方法用来获取促销的方式,于是PromoteSalesService.java
的代码变成了如下样子
package com.info.examples.strategy;
import java.math.BigDecimal;
public interface PromoteSalesService {
/**
* 获取商品促销价
*
* @param originalPrice 原价
* @return 商品的促销价
*/
BigDecimal getPromotionPrice(BigDecimal originalPrice);
/**
* 获取促销方式
*
* @return 促销方式
*/
PromoteTypeEnum getType();
}
实现接口
package com.info.examples.strategy;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
/**
* 打折促销
*/
@Service
public class DiscountPromoteServiceImpl implements PromoteSalesService {
// 满100打9折,199以上打85折
@Override
public BigDecimal getPromotionPrice(BigDecimal originalPrice) {
if (new BigDecimal("100").compareTo(originalPrice) > 0) {
return originalPrice;
} else if (new BigDecimal("199").compareTo(originalPrice) >= 0) {
return originalPrice.multiply(new BigDecimal("0.9"));
}
return originalPrice.multiply(new BigDecimal("0.85"));
}
@Override
public PromoteTypeEnum getType() {
return PromoteTypeEnum.DISCOUNT;
}
}
package com.info.examples.strategy;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
/**
* 满减促销
*/
@Service
public class FullReductionPromoteServiceImpl implements PromoteSalesService {
// 满 199 减 50
@Override
public BigDecimal getPromotionPrice(BigDecimal originalPrice) {
if (new BigDecimal("199").compareTo(originalPrice) > 0) {
return originalPrice;
}
return originalPrice.subtract(new BigDecimal("50"));
}
@Override
public PromoteTypeEnum getType() {
return PromoteTypeEnum.FULL_REDUCTION;
}
}
接下来是本文的核心,实现策略模式的上下文。
package com.info.examples.strategy;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class PromoteStrategyFactory implements ApplicationContextAware {
private Map<PromoteTypeEnum, PromoteSalesService> PROMOTE_MAP = new ConcurrentHashMap<>(1 << 2);
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
// 通过 ApplicationContext 获取到所有 PromoteSalesService 的实例对象
final Map<String, PromoteSalesService> beans = context.getBeansOfType(PromoteSalesService.class);
// 把促销类型和具体的实现类形成映射关系存储到map
beans.values().forEach(bean -> PROMOTE_MAP.put(bean.getType(), bean));
}
/**
* 根据促销类型获取对应的实现
*
* @param promoteType
* @return
*/
public PromoteSalesService getInstance(PromoteTypeEnum promoteType) {
return PROMOTE_MAP.get(promoteType);
}
}
主体编写完毕,开始测试:
package com.info.examples.controller;
import com.info.examples.strategy.PromoteSalesService;
import com.info.examples.strategy.PromoteStrategyFactory;
import com.info.examples.strategy.PromoteTypeEnum;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.Random;
@RestController
public class PromoteController {
private final PromoteStrategyFactory strategyFactory;
public PromoteController(PromoteStrategyFactory strategyFactory) {
this.strategyFactory = strategyFactory;
}
@GetMapping("/getPrice/{price:^[1-9]\\d*$|^(?>[1-9]\\d*\\.\\d{1,8})$}")
public String getPromotePrice(@PathVariable String price) {
PromoteTypeEnum promoteType = new Random().nextInt(100) > 50 ? PromoteTypeEnum.FULL_REDUCTION : PromoteTypeEnum.DISCOUNT;
final PromoteSalesService promoteSalesService = strategyFactory.getInstance(promoteType);
final BigDecimal promotionPrice = promoteSalesService.getPromotionPrice(new BigDecimal(price));
return String.format("原价 %s 使用 %s 促销后价格为 %f", price, promoteSalesService.getType(), promotionPrice.doubleValue());
}
}
启动项目,打开浏览器输入http://localhost:8080/getPrice/200
就可以进行测试啦。
好了今天的分享就到这里,希望对你有帮助。打完收工!