策略模式实现多种支付方式
一、策略模式是什么?
定义
策略模式属于对象的行为模式。一个类的行为或其算法可以在运行是更改,这种类型的设计模式就属于行为模式。
简单来讲就是不同场景有不同的策略,不同场景下对同一行为有不同实现
二、实现多种支付方式
需求场景:我们在写订单支付场景的代码时,客户可以选择多种支付方式,有银联支付、支付宝支付、微信支付等等。
1.传统方式
代码如下(示例):
/**
* 订单类
* @author gsz
* @date 2022/02/15 16:37
*/
public class Order {
// 订单id
private String orderId;
// 支付方式
private String payType;
// 订单金额
private long amount;
public Order(String orderId, String payType, long 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 ("unionPay".equals(payType)) {
System.out.println("银联支付,订单号为:" + orderId + " ,支付金额:" + amount);
payment = true;
} else if ("wechatPay".equals(payType)) {
System.out.println("微信支付,订单号为:" + orderId + " ,支付金额:" + amount);
payment = true;
}
return payment;
}
}
测试:
@Test
public void test() {
String orderId = "123456";
String payType = "aliPay";
long amount = 500;
// 创建订单
Order order = new Order(orderId, payType, amount);
// 支付
order.pay();
}
结果:
支付宝支付,订单号为:123456,支付金额:500
可以看出这段代码在逻辑上没有问题,也能够很好的运行;
缺点:将所有的支付方式都写在同一个方法里面,显得有点臃肿,还带来了一个扩展的问题
如果我们再增加一种支付方式,就不得不修改这段代码,再增加一条 if…else,这就降低了代码的可维护性。
违背了开闭原则(软件实体应该对扩展开放,对修改关闭。也就是说软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化)。
那能否有一种方法能让我们既不修改主要逻辑代码的前提下让代码变得更优雅也能很好的对其进行扩展呢?那不防我们一起来看看今天的主角:策略模式
2.策略模式实现
代码如下(示例):
创建抽象的支付接口 Payment ,让不同的支付逻辑类都实现此接口
/**
* 统一支付接口
* @author gsz
* @date 2022/02/15 16:15
*/
public interface Payment {
boolean pay(String orderId, Long amount);
}
支付宝 支付类
/**
* 支付宝支付
* @author gsz
* @date 2022/02/15 16:16
*/
public class AliPay implements Payment {
@Override
public boolean pay(String orderId, Long amount) {
System.out.printf("--%s--", "支付宝 支付");
return true;
}
}
微信 支付类:
/**
* 微信 支付
* @author gsz
* @date 2022/02/15 16:18
*/
public class WeChatPay implements Payment {
@Override
public boolean pay(String orderId, Long amount) {
System.out.printf("--%s--", "微信 支付");
return true;
}
}
银联 支付类:
/**
* 银联 支付
* @author gsz
* @date 2022/02/15 16:19
*/
public class UnionPay implements Payment {
@Override
public boolean pay(String orderId, Long amount) {
System.out.printf("--%s--", "银联 支付");
return true;
}
}
然后创建订单类: Order
/**
* 订单类
* @author gsz
* @date 2022/02/15 16:37
*/
public class Order {
// 订单id
private String orderId;
// 金额
private long amount;
// 具体支付类型的引用
private Payment payType;
public Order(String orderId, Payment payType, long amount) {
this.orderId = orderId;
this.payType = payType;
this.amount = amount;
}
/**
* 订单支付方法
*
* @return
*/
public boolean pay() {
boolean paySuccess;
// 调用支付接口
paySuccess = payType.pay(orderId, amount);
if (!paySuccess) {
// 支付失败逻辑
System.out.println("支付失败!");
}
return paySuccess;
}
}
测试:
@Test
public void test() {
// 创建支付策略
Payment weChatPay = new WeChatPay();
// 创建策略上下文(订单),并将具体的策略实现注入
String orderId = "123456";
long amount = 150;
Order order = new Order(orderId, weChatPay, amount);
// 调用具体支付策略逻辑
order.pay();
}
结果:
微信 支付
通过 策略模式 改造之后,我们的订单支付的扩展变得非常容易,增加支付方式时,直接创建一个类并实现支付逻辑,无需在修改主类 Order 。这也就遵循了 开闭原则
3.策略模式 + 工厂模式
上面的改造,只能勉强说明 策略模式 给实际业务带来了好处,但是回到我们现实的支付场景中,用户选择支付方式都是在前段页面进行的,而且大多都是 前后端分离 的模式,不能够像 JSP 那样在页面中创建 JAVA 对象,所有参数都是从 页面构建好建值对传入,那么我们就可以结合 工厂模式 继续改造。
创建支付策略的工厂类:
/**
* 支付策略工厂
* @author gsz
* @date 2022/02/15 10:32
*/
public class PayStrategyFactory {
// 支付方式常量
public static final String ALI_PAY = "1";
public static final String WECHAT_PAY = "2";
public static final String UNION_PAY = "3";
private static Map<String, Payment> PAYMENT_STRATEGY_MAP = new HashMap<>();
static {
PAYMENT_STRATEGY_MAP.put(ALI_PAY, new AliPay());
PAYMENT_STRATEGY_MAP.put(UNION_PAY, new UnionPay());
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;
}
}
然后结合实际情况对 Order 类进行修改,让支付方式的选择权交由用户做支付动作时进行选择:
/**
* 订单类
* @author gsz
* @date 2022/02/15 16:37
*/
public class Order {
// 订单id
private String orderId;
// 金额
private long amount;
// 具体支付类型的引用
private String payType;
public Order(String orderId, long amount) {
this.orderId = orderId;
this.amount = amount;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public long getAmount() {
return amount;
}
public void setAmount(long amount) {
this.amount = amount;
}
public String getPayType() {
return payType;
}
public void setPayType(String payType) {
this.payType = payType;
}
}
最后测试代码:
/**
* @author gsz
* @date 2022/02/15 13:20
*/
@RestController
@RequestMapping("pay")
public class PayController {
@GetMapping
public boolean pay(Order order) {
Payment payment = PayStrategyFactory.getPayment(order.getPayType());
boolean pay = payment.pay(order.getOrderId(), order.getAmount());
return pay;
}
}
返回给前段的结果:
true
这样才算完成了一个比较友好且更贴合实际业务情况的业务代码。当然这只是简单的一个示例。
总结
应用场景
1. 一个系统中需要动态的在几种算法中选择其中一种
2.在有多种算法相似的情况下,解决使用 if…else 所带来的复杂和难以维护的问题
优缺点
优点:
1. 策略模式符合开闭原则
2. 避免了代码中过多的 if...else 和switch 语句的出现
3. 使用策略模式可以提高算法的保密性和安全性
缺点
1. 客户端必须知道拥有的策略,并决定使用哪一种
2. 代码中会出现比较多的策略类,增加维护难度