策略模式实现多种支付方式
首先我们要来了解什么是策略模式
定义
策略模式属于对象的行为模式。其中用意是针对一组算法,将每一个算法封装到具有共同接口的独立类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化,也就是在策略模式中,一个类的行为或其算法可以在运行时更改。
举例 不使用策略模式时实现多种支付方式
/**
* 订单类
*/
public class Order {
// 订单id
private String orderId;
// 支付方式
private String payType;
// 订单金额
private BigDecimal amount;
public Order(String orderId, String payType, BigDecimal amount) {
this.orderId = orderId;
this.payType = payType;
this.amount = amount;
}
/**
* 订单支付方法
*
* @return
*/
public boolean pay() {
// 是否支付成功
boolean payment = false;
if ("aliPay".equals(payType)) {
System.out.println("用户选择 支付宝 支付,订单号为:" + orderId + " ,支付金额:" + amount);
payment = true;
} else if ("jdPay".equals(payType)) {
System.out.println("用户选择 京东 支付,订单号为:" + orderId + " ,支付金额:" + amount);
payment = true;
} else if ("wechatPay".equals(payType)) {
System.out.println("用户选择 微信 支付,订单号为:" + orderId + " ,支付金额:" + amount);
payment = true;
}
return payment;
}
}
测试类:
/**
* @author: DIANWEI
* @create: 2021/12/30
* @Description:
* @FileName: test
* @History:
* @自定义内容:
*/
public class test {
@Test
public void test2(){
String orderId = "1";
String payType = "wechatPay";
BigDecimal amount = new BigDecimal(1);
// 创建订单
Order order = new Order(orderId, payType, amount);
// 支付
order.pay();
}
}
结果:
用户选择 微信 支付,订单号为:1 ,支付金额:1
可以看出这段代码在逻辑上没有问题,也能够很好的运行;
但是存在一个问题:将所有支付方式都卸载同一个方法里面,显得有点臃肿,还带来了一个扩展问题,如果我们再增加一种支付方式,我们就不得不修改这段代码,
再增加一条 if语句,这就将低了代码的可维护性。违背了开闭原则。(开闭原则:一个软件实体(如类,模块和函数)应该对扩展开放,对修改关闭。)
结构
策略模式中一般会涉及到三个角色:
策略接口角色 Payment:用来约束一系列具体的策略算法,策略上下文角色 PayStrategyFactory使用此策略接口来调用具体的策略所实现的算法。
具体策略实现角色 AliPay,JdPay,WeChatPay:具体的策略实现,即具体的算法实现。
策略上下文角色 PayStrategyFactory:策略上下文,负责和具体的策略实现交互,通常策略上下文对象会持有一个真正的策略实现对象,策略上下文还可以让具体的策略实现从其中获取相关数据,回调策略上下文对象的方法。
在简单的了解了策略模式之后,再看看文章开头的实例场景,我们使用策略模式来对其进行改造:
我们将订单支付逻辑中的各种支付场景算法单独抽离出来:
使用策略模式
订单类:
/**
* @author: DIANWEI
* @create: 2022/2/16
* @Description: 订单类,拥有一个支付方法
* @FileName: Order
* @History:
* @自定义内容:订单类,拥有一个支付方法
*/
public class Order {
// 订单id
private String orderId;
// 金额
private BigDecimal amount;
public Order(String orderId, BigDecimal amount) {
this.orderId = orderId;
this.amount = amount;
}
/**
* 订单支付方法
*
* @return
*/
public boolean pay(String payType) {
boolean paySuccess;
Payment payment = PayStrategyFactory.getPayment(payType);
// 调用支付接口
paySuccess = payment.pay(orderId, amount);
if (!paySuccess) {
// 支付失败逻辑
System.out.println("支付失败!");
}
return paySuccess;
}
统一支付接口:
/**
* @author: DIANWEI
* @create: 2022/2/17
* @Description: 统一支付接口
* @FileName: Payment
* @History:
* @自定义内容: 统一支付接口
*/
public interface Payment {
boolean pay(String orderId, BigDecimal amount);
}
支付宝支付:
/**
* @author: DIANWEI
* @create: 2022/2/17
* @Description: 支付宝支付
* @FileName: AliPay
* @History:
* @自定义内容:支付宝支付
*/
@Service
public class AliPay implements Payment{
@Override
public boolean pay(String orderId, BigDecimal amount) {
System.out.println("用户选择 支付宝 支付,订单号为:" + orderId + " ,支付金额:" + amount);
return true;
}
}
京东支付:
/**
* @author: DIANWEI
* @create: 2022/2/17
* @Description: 京东支付
* @FileName: JdPay
* @History:
* @自定义内容:京东支付
*/
@Service
public class JdPay implements Payment{
@Override
public boolean pay(String orderId, BigDecimal amount) {
System.out.println("用户选择 京东 支付,订单号为:" + orderId + " ,支付金额:" + amount);
return true;
}
}
微信支付
/**
* @author: DIANWEI
* @create: 2022/2/17
* @Description: 微信支付
* @FileName: WeChatPay
* @History:
* @自定义内容:
*/
@Service
public class WeChatPay implements Payment{
@Override
public boolean pay(String orderId, BigDecimal amount) {
System.out.println("用户选择 微信 支付,订单号为:" + orderId + " ,支付金额:" + amount);
return true;
}
}
支付策略类:
/**
* 支付策略类
*
*/
public class PayStrategyFactory {
// 支付方式常量
public static final String ALI_PAY = "aliPay";
public static final String JD_PAY = "jdPay";
public static final String WECHAT_PAY = "wechatPay";
// 支付方式管理集合
private static Map<String, Payment> PAYMENT_STRATEGY_MAP = new HashMap<>();
static {
PAYMENT_STRATEGY_MAP.put(ALI_PAY, new AliPay());
PAYMENT_STRATEGY_MAP.put(JD_PAY, new JdPay());
PAYMENT_STRATEGY_MAP.put(WECHAT_PAY, new WeChatPay());
}
/**
* 获取支付方式类
*
* @param payType 前端传入支付方式
* @return
*/
public static Payment getPayment(String payType) {
Payment payment = PAYMENT_STRATEGY_MAP.get(payType);
if (payment == null) {
throw new NullPointerException("支付方式选择错误!");
}
return payment;
}
}
测试:
/**
* @author: DIANWEI
* @create: 2021/12/30
* @Description:
* @FileName: test
* @History:
* @自定义内容:
*/
public class test {
@Test
public void test2(){
// 前端传入的参数
String orderId = "110";
String payType = PayStrategyFactory.ALI_PAY;
BigDecimal amount = new BigDecimal(119);
// 创建策略上下文(订单),并将具体的策略实现注入
Order order = new Order(orderId, amount);
// 实际情况是 在支付的时候选择支付方式,因为有可能先提交了订单,后面再付款
order.pay(payType);
}
}
结果:
用户选择 支付宝 支付,订单号为:110 ,支付金额:119
可以看到,在经过使用 策略模式 改造之后, 订单支付的扩展变得非常的容易,增加支付方式时,直接创建一个类并实现支付逻辑即可,不需要再修改 主类 Order。这就遵循了 开闭原则 。
总结
应用场景
- 假如系统中有很多类,而他们的区别仅仅只是他们之间的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为
- 一个系统需要动态地在几种算法中选择一种
- 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现
常用来解决的问题
在有多种算法相似的情况下,解决使用 if...else 所带来的复杂和难以维护的问题
优缺点
优点
- 策略模式符合开闭原则
- 避免了代码中过多的 if...else 和switch 语句的出现
- 使用策略模式可以提高算法的保密性和安全性
缺点
- 客户端必须知道素有的策略,并决定使用哪一种 代码中会出现比较多的策略类,增加维护难度