Spring Boot 项目中实现策略模式

465 阅读3分钟

介绍

策略模式是一种行为模式,可以替代大量 if-else,使自己的代码更加优雅,更加容易进行扩展和兼容处理,可以帮我们解决具有同类但是行为有异的问题。

结构

image.png

  • Strategy 定义所有支持的算法公共接口,Context使用这个接口调用具体策略类的实现
  • StrategyImpl-X 实现了Strategy接口,实现具体的算法
  • Context 上下文对象,屏蔽调用方对具体策略的直接调用,用于提取具体的策略

案例

现在需要实现一个不同的支付方式功能,有微信支付,支付宝支付,银行卡支付等,后续根据需求变动进行扩展等,这样的场景通常应if-else可以实现,但是会形成硬编码,不利于扩展,因此可以通过策略模式来解决

public void pay(String mode) {
    if(mode == "aliPay") {
        // System.out.println("支付宝支付")
    } else if(mode == "weChat"){
         // System.out.println("微信支付")
    } else if(mode == "bankCard"){
         // System.out.println("银行卡支付")
    } else {
        // System.out.println("支付方式有误")
    }
    // 后续有其他支付方式可以在添加if-else
}

image.png

策略模式实现

支付策略接口

/**
 * @author xiangjin.kong
 * @date 2021/4/8 21:57
 * @desc 支付接口 - 策略接口
 */
public interface PayStrategy {

    /**
     * 支付方式
     * @return
     */
    public String getMode();

    /**
     * 支付接口
     * @param type
     * @param price
     */
    void pay(BigDecimal price);
}

具体支付实现接口需要将他们注入到Spring容器中

支付宝支付接口实现

 */
@Slf4j
@Component
public class AliPay implements PayStrategy {

    @Override
    public String getMode() {
        return "aliPay";
    }

    @Override
    public void pay(BigDecimal price) {
        log.info("一大堆业务代码.....");
        log.info("支付宝支付,支付了{}元", price);
    }
}

微信支付接口实现

@Slf4j
@Component
public class WeChatPay implements PayStrategy {
    @Override
    public String getMode() {
        return "weChat";
    }

    @Override
    public void pay(BigDecimal price) {
        log.info("一大堆业务代码.....");
        log.info("微信支付,支付了{}元", price);
    }
}

银行卡支付

@Slf4j
@Component
public class BankCardPay implements PayStrategy {
    @Override
    public String getMode() {
        return "bankCard";
    }

    @Override
    public void pay(BigDecimal price) {
        log.info("一大堆业务代码.....");
        log.info("银行卡支付,支付了{}元", price);
    }
}

支付策略控制类

@Component
public class PayStrategyContext {

    private final Map<String, PayStrategy> strategyMap = new ConcurrentHashMap<String, PayStrategy>();

    /**
     * 构造函数,把spring容器中所有关于该接口的子类,全部放入到集合中
     * @param payStrategyList
     */
    public PayStrategyContext(List<PayStrategy> payStrategyList) {
        for (PayStrategy payStrategy : payStrategyList) {
            strategyMap.put(payStrategy.getMode(), payStrategy);
        }
    }
   
//    @Autowired
//    List<PayStrategy> payStrategyList;
 
    /**
     * Autowired当使用在Collection里时,会将所申明类的所有实现类都放在那个指定的Collection里
     * 如果Autowired和map使用的话呢,它将它bean的名称作为key,所有的bean作为value
     */
//    @Autowired
//    Map<String, PayStrategy> payStrategyMap;


    public void useStrategy(String mode, BigDecimal price) {
        PayStrategy strategy = this.strategyMap.get(mode);
        if (strategy == null) {
            throw new RuntimeException("支付方式有误,请检查");
        }
        strategy.pay(price);
    }
}
  • 策略模式的控制类主要是外部可以传递不同的策略实现,再通过统一的方法执行策略
  • 这里也可以包装成Map<String, Strategy>结构,这里的key是策略实现类的bean名称,Strategy是具体的策略实现类
  • 当需要新增支付方式,只需要新增支付方式类实现支付接口即可

controller

@RestController
public class PayController {

    @Autowired
    PayStrategyContext context;

    @GetMapping
    public void test(@RequestParam(value = "mode") String mode) {
        context.useStrategy(mode, new BigDecimal(10));
    }
}

测试验证

支付宝支付 image.png

image.png

微信支付

image.png image.png

总结

使用策略模式可以把if-else语句很好的优化掉,大量的if语句的使用会让代码难以扩展和维护,使用策略模式可以很好的满足隔离性和扩展性,可以应对更多易变的需求。

github项目链接 https://github.com/kong0827/Design-Patterns