定义
Define a family of algorithms,encapsulate each one,and make them interchangeable.
定义一组算法将每个算法封装起来,并且使他们之间可以互换。
策略模式同样可以用来解决责任链模式中一堆if else的问题。
类图和代码
抽象出一个策略接口声明一个需要做的算法操作,以及一个上下文context用于执行算法。
这里我模拟一个原来项目中用到的操作:前端传入订单和支付方式,后端调用对应的策略向微信,支付宝或者银联提供的接口调用支付。为了简化操作,这里的订单我使用支付金额描述(实际项目中订单金额应通过后台系统进行计算),也去除了调用预支付下单接口的操作。
策略接口:
1 | public interface IPayStrategy { |
分别写策略去实现:
1 | public class AliPay implements IPayStrategy { |
其他微信、银联支付与之类似。
策略上下文,用于支付策略的执行:
1 | public class StrategyContext { |
到这里为止,类图中画的这么多就基本完成了,但是很明显有一个问题:由于上下文中只负责调用托管的策略中的支付方法,所以我们还需要创建策略并托管给上下文。所以这里我打算使用一个策略枚举保存策略的种类,使用最简单的工厂方法创建一个策略,这样就避免了我们的业务模块和算法模块有太多的耦合。
策略枚举如下:
1 | public enum StrategyEnums { |
工厂方法根据枚举类型创建策略:
1 | public class StrategyFactory { |
可能有人会觉得这里同样有大量的case或者if else,但是我的理解是:必要的细节逻辑还是要有的,但是要在业务代码中隐藏起来,细节交由承担该职责的类来处理。
现在创建策略和执行策略的上下文都已经完成了,现在我们需要做的步骤是:根据支付参数获得支付枚举,再利用支付枚举创建对应策略,然后委托给上下文执行这个策略。通常情况下,我们的支付参数很可能是一个标志,而不是一个枚举,所以很多情况下我们可能需要一个util或者helper类帮助我们将标志转化为枚举,假如前端传入的是字符串类型参数,工具类可以是:
1 | public class PayEnumsConvertUtil { |
可以将标志到枚举的过程放入到门面类中,使得调用方只需要调用,而无需处理任务细节,这里我再加上一个门面模式的类:
1 | public class HandlerFaced { |
调用方调用就很简单了:
1 | public class Controller { |
执行结果
调用了支付宝支付接口,支付5元
借助Spring
策略模式同样可以借助Spring来装一波。
首先将所有实现类加上@Component注解(@Service也行,本质差不多)并为注解中value赋值:
1 | "ali") ( |
其他银联支付策略和微信支付策略类似。
接下来,我们无需门面类进行委托调用,只需要注入上下文类执行pay方法即可:
1 |
|
测试类如下:
1 | (SpringRunner.class) |
执行结果:
调用了银联支付接口,支付10元
调用了微信支付接口,支付20元
总结
优点:算法自由切换;避免代码臃肿;扩展性良好,很符合开闭原则;
缺点:类数量膨胀。