前言
策略模式属于行为型模式,是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
组成
上下文类(PayContext): 维护一个对PayStrategy对象的引用。
抽象策略类(PayStrategy): 定义所有支持的算法的公共接口或抽象类
具体策略类(AliPayStrategy、JdPayStrategy、WechatPayStrategy): 封装了具体的算法或行为,继承于PayStrategy
场景以支付方式为例
1. 支付类型枚举
public enum PayEnum {
JD,
ALIPAY,
WECHAT
}
2. 策略抽象类
public abstract class PayStrategy {
public abstract PayEnum payType();
public abstract void pay();
}
3. 具体策略实现类
@Component
public class AliPayStrategy extends PayStrategy{
@Override
public PayEnum payType() {
return PayEnum.ALIPAY;
}
@Override
public void pay() {
System.out.println("使用支付宝支付");
}
}
@Component
public class JdPayStrategy extends PayStrategy{
@Override
public PayEnum payType() {
return PayEnum.JD;
}
@Override
public void pay() {
System.out.println("使用京东支付");
}
}
@Component
public class WechatPayStrategy extends PayStrategy{
@Override
public PayEnum payType() {
return PayEnum.WECHAT;
}
@Override
public void pay() {
System.out.println("使用微信支付");
}
}
3. 策略上下文
public class PayContext{
private PayStrategy payStrategy;
public PayContext(PayStrategy payStrategy){
this.payStrategy = payStrategy;
}
public void pay(){
payStrategy.pay();
}
}
4. 单元测试
@SpringBootTest
class StrategyDemoApplicationTests {
@Test
void contextLoads() {
PayContext payStrategy =new PayContext(new AliPayStrategy());
payStrategy.pay();
}
}
5. 运行结果
通过实例化不同的策略类会使用不同的支付方式 ,但个人不太推荐使用这种方式(需要在客户端调用通过类型if else 来实例化对应的策略类)
优化:比如我只需要传入一个参数即可,大话设计模式一书中添加了简单工厂模式把if else 或 switch的判定写在了 context上下文中,将业务判定进行了隔离,但还是不太完美,如果新增一个策略类后 还需更改context上下文的switch条件。
如有对简单工厂模式不太了解的可以阅读 https://blog.csdn.net/javanbme/article/details/114922029 。
如何更好的实现? 话不多说直接上示例 。
1. 新增支付策略工厂
@Component
public class PayStrategyFactory implements ApplicationContextAware {
private Map<PayEnum,PayContext> payStrategyMap = new HashMap<>();
public PayContext getPayStrategy(PayEnum payEnum){
if (!payStrategyMap.containsKey(payEnum)) {
throw new IllegalArgumentException("PayContext not exist");
}
return payStrategyMap.get(payEnum);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, PayStrategy> beansOfType = applicationContext.getBeansOfType(PayStrategy.class);
beansOfType.forEach((k, payStrategy) -> {
payStrategyMap.put(payStrategy.payType(),new PayContext(payStrategy));
});
}
}
2. 修改单元测试方法
@SpringBootTest
class StrategyDemoApplicationTests {
@Autowired
private PayStrategyFactory payStrategyFactory;
@Test
void contextLoads() {
PayContext payStrategy = payStrategyFactory.getPayStrategy(PayEnum.ALIPAY);
payStrategy.pay();
}
}
运行结果和上述一致,而该方式只需指定不同的枚举类型即可, 当添加新的策略类时,无需更改工厂类和上下文! 即核心是使用了Spring的ApplicationContextAware类
不太了解的可以阅读 https://blog.csdn.net/javanbme/article/details/116096885
源码: https://download.csdn.net/download/javanbme/18274617