介绍
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
举例:诸葛亮给了赵云三个锦囊,每个锦囊都有一个策略。选择交通工具汽车、火车、飞机都是一种策略。
注意事项:
当我们的if else中需要处理很多业务,就可以考虑使用策略模式;
如果一个系统的策略过多,就需要考虑使用混合模式,解决策略类膨胀的问题。
实现
这里以不同等级的客户购买物品时对应不同的折扣为例,先创建一个接口
public interface Strategy {
Double calculatePrice(Double price,Integer amount);
}
具体的策略类
public class GeneralCustomer implements Strategy {
@Override
public Double calculatePrice(Double price, Integer amount) {
return price * amount;
}
}
public class SilverCustomer implements Strategy {
@Override
public Double calculatePrice(Double price, Integer amount) {
return price * amount * 0.99;
}
}
public class GoldCustomer implements Strategy {
@Override
public Double calculatePrice(Double price, Integer amount) {
return price * amount * 0.97;
}
}
创建 Context 类。上下文并不执行任务, 而是负责与具体的策略类交互。这就好比出行工具有汽车、火车、飞机,但是谁去开火车?就是这个context
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public Double executeStrategy(Double price,Integer amount){
return strategy.calculatePrice(price, amount);
}
}
使用
public class StrategyDemo {
public static void main(String[] args) {
double totalPrice = 0;
Context context = new Context(new GeneralCustomer());
totalPrice = context.executeStrategy(15.0, 2);
System.out.println(totalPrice);
Context context1 = new Context(new SilverCustomer());
totalPrice = context1.executeStrategy(15.0,2);
System.out.println(totalPrice);
Context context2 = new Context(new GoldCustomer());
totalPrice = context2.executeStrategy(15.0, 2);
System.out.println(totalPrice);
}
}
问题
1、还是要去if else 判断客户的等级然后决定使用哪种策略,如何解决这个问题?
2、策略类会增多,业务逻辑分散到各个实现类中,而且没有一个地方可以俯视整个业务逻辑
扩展
策略工厂模式
上一篇我们说到的工厂模式,我给一个标识,由工厂帮我创建对应的产品,那么结合到这里是不是就可以解决上述问题? 我给工厂一个客户等级标识,它会给你对应的策略。
创建策略工厂,Context类就用不上了:
public class StrategyFactory {
public static Strategy execute(Integer levelCode){
Strategy strategy = null;
switch (levelCode){
case 1:
strategy = new GeneralCustomer();
break;
case 2:
strategy = new SilverCustomer();
break;
case 3:
strategy = new GoldCustomer();
break;
default:
throw new IllegalArgumentException("客户等级错误");
}
return strategy;
}
}
使用
public class StrategyDemo {
public static void main(String[] args) {
//接口参数获取用户等级 传给策略工厂即可
Strategy general = StrategyFactory.execute(1);
general.calculatePrice(15.0,2);
}
}
如上我们可以解决第一个问题,那么第二个问题又该如何解决,策略类膨胀会导致项目非常臃肿。
Map+函数式接口
此时我们把上诉的策略类全部作为方法
@Service
public class CustomerStrategyService {
public Double generalCustomer(Double price, Integer amount) {
return price * amount;
}
public Double silverCustomer(Double price, Integer amount) {
return price * amount * 0.99;
}
public Double goldCustomer(Double price, Integer amount) {
return price * amount * 0.97;
}
}
Map+Function优化
@Service
public class PriceService {
@Autowired
private CustomerStrategyService customerStrategyService;
@FunctionalInterface
interface CalculateFunction<A,B>{
public Double calculate(A a,B b);
}
private final Map<Integer, CalculateFunction<Double,Integer>> strategyMap = new HashMap<>();
/**
* 初始化策略
*/
@PostConstruct
public void initDispatcher(){
strategyMap.put(1,(price,amount) -> customerStrategyService.generalCustomer(price,amount));
strategyMap.put(2,(price,amount) -> customerStrategyService.silverCustomer(price,amount));
strategyMap.put(3,(price,amount) -> customerStrategyService.goldCustomer(price,amount));
}
public Double calculatePrice(Integer customerLevel,Double price,Integer amount){
CalculateFunction<Double, Integer> function = strategyMap.get(customerLevel);
//这里防止客户等级没找到匹配的,可以使用断言来判断从而抛出统一异常
return function.calculate(price,amount);
}
}
搞个接口试试
@RestController
public class TestController {
@Autowired
private PriceService priceService;
@RequestMapping("/calculatePrice")
public Double calculatePrice(Integer customerLevel,Double price,Integer amount){
return priceService.calculatePrice(customerLevel, price, amount);
}
}
总结
少于4种情况且每种情况对应的业务逻辑不复杂那肯定首选if else,
有4-6种情况且业务逻辑不复杂,建议使用switch case
业务逻辑复杂的情况下,可使用策略工厂模式或者Map+Function,自行选择。