设计模式-策略模式初体验和总结
设计模式按照功能型分类
创建型模式:
- 工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构型模式:
- 适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
行为型模式:
-
策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
策略模式的定义:
-
定义了算法族,分别封装起来,让它们直接可以互相替换,此模式让算法的变化独立于使用算法的客户.让算法的变化不会影响算法的用户.
-
适用场景: 对外提供一个统一的接口,有不同的业务实现,上层不需要关心具体的实现细节,比如支付场景,消息消费场景
-
可以避免多重分支 if else 或者switch分支
-
属于行为型模式
适用场景举例:
- 订单支付场景
/**
* 支付订单
*/
@Data
@Accessors(chain = true)
public class PayOrder {
/**
* 订单号
*/
private String orderId;
/**
* 支付用户
*/
private String userId;
/**
* 支付金额
*/
private Long payAmount;
/**
* 支付渠道
*/
private PayChannelEnums payChannel;
<span class="hljs-keyword">public PayOrder(<span class="hljs-built_in">String orderId, <span class="hljs-built_in">String userId, Long payAmount, PayChannelEnums payChannel) {
<span class="hljs-keyword">this.orderId = orderId;
<span class="hljs-keyword">this.userId = userId;
<span class="hljs-keyword">this.payAmount = payAmount;
<span class="hljs-keyword">this.payChannel = payChannel;
}
/**
-
支付类型枚举
*/
public enum PayChannelEnums {
/**
- 支付渠道
*/
WE_CHAT("wechat", "微信支付"),
ALI_PAY("alipay", "支付宝支付"),
JD_PAY("jdpay", "京东白条"),
UNION_PAY("unionpay", "银联支付");
private String code;
private String desc;
PayChannelEnums(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
/**
-
支付接口
*/
public interface Payment {
/**
- 标准支付方法
*/
Boolean doPay(PayOrder payOrder);
/**
- 校验余额,钩子方法
*/
Boolean checkalance(PayOrder payOrder);
}
/**
-
支付宝支付
*/
public class AliPay implements Payment {
static {
System.out.println("欢迎使用阿里支付");
}
@Override
public Boolean doPay(PayOrder payOrder) {
return true;
}
@Override
public Boolean checkBalance(PayOrder payOrder) {
return payOrder.getPayAmount() > 10;
}
}
/**
-
微信支付
*/
public class WeChatPay implements Payment {
static {
System.out.println("欢迎使用微信支付");
}
@Override
public Boolean doPay(PayOrder payOrder) {
return true;
}
@Override
public Boolean checkBalance(PayOrder payOrder) {
return payOrder.getPayAmount() > 50;
}
}
/**
-
默认执行的实现类
*/
public class DefaultPayment implements Payment {
@Override
public Boolean doPay(PayOrder payOrder) {
Payment payment = getPaymentByType(payOrder.getPayChannel());
if (payment == null) {
System.out.println("不支持的支付类型");
return false;
}
//校验余额
Boolean checkalance = payment.checkBalance(payOrder);
if (!checkalance) {
System.out.println("您的余额不足,无法支付");
return false;
}
Boolean result = payment.doPay(payOrder);
if (!result) {
System.out.println("支付失败");
} else {
System.out.println("支付成功");
}
return result;
}
/**
- 获取实际的支付类型,也可以通过spring上下文获取,或者通过bean工厂创建
- @param payChannelEnums
- @return
*/
private Payment getPaymentByType(PayChannelEnums payChannelEnums) {
try {
if (Objects.equals(payChannelEnums, PayChannelEnums.WE_CHAT)) {
return new WeChatPay();
}
if (Objects.equals(payChannelEnums, PayChannelEnums.ALI_PAY)) {
return new AliPay();
}
if (Objects.equals(payChannelEnums, PayChannelEnums.JD_PAY)) {
return new JdPay();
}
if (Objects.equals(payChannelEnums, PayChannelEnums.UNION_PAY)) {
return new UnionPay();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public Boolean checkBalance(PayOrder payOrder) {
return false;
}
}
/**
-
支付控制台
*/
public interface PaymentPlatform {
/**
- 订单支付入口
- @param payOrder
- @return
*/
Boolean orderPay(PayOrder payOrder);
}
/**
-
支付控制台实现类
*/
public class PaymentPlatformImpl implements PaymentPlatform {
@Override
public Boolean orderPay(PayOrder payOrder) {
//默认执行的实现类实现,不需要关心支付细节
Payment defaultPayment = new DefaultPayment();
return defaultPayment.doPay(payOrder);
}
}
/**
-
执行main方法
*/
public class MainClass {
public static void main(String[] args) {
PayOrder payOrder = new PayOrder("O-0001", "lihua", 20L, PayChannelEnums.ALI_PAY);
PaymentPlatform paymentPlatform = new PaymentPlatformImpl();
paymentPlatform.orderPay(payOrder);
}
}
/**
- 控制台输出
*/
欢迎使用阿里支付
支付成功
注意点
-
支付控制台 PaymentPlatform提供了外部访问的标准入口和参数,内部封装了具体的执行细节
-
payment是支付的标准接口,通过DefaultPayment的getPaymentByType方法来实现创建支付bean和调用支付方法,也可以通过bean工厂和spring上下文来创建bean,策略模式一般会结合工厂模式使用
-
doPay是为了方便测试,默认返回了支付结果,在实际支付过程中,支付调用一般是先调用预支付接口获取预支付信息,前端唤起支付后,后端服务通过异步获取第三方支付回调的支付结果
支付类工厂代码如下
/**
* 支付类工厂
*/
@Component
public class PaymentFactory implements ApplicationContextAware {
private static final Map<String, Payment> paymentMap = new HashMap<>();
public static Payment getInstance(PayChannelEnums payChannelEnums) {
return paymentMap.get(payChannelEnums.getCode());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Payment> controllableBeans = applicationContext.getBeansOfType(Payment.class);
for (Map.Entry<String, Payment> entry : controllableBeans.entrySet()) {
if (entry.getValue().equals(this)) {
continue;
}
paymentMap.put(entry.getKey(), entry.getValue());
}
}
}
/**DefaultPayment调用**/
Payment payment = PaymentFactory.getInstance(payOrder.getPayChannel());
策略模式的优缺点
优点
-
策略模式复合开闭原则,如果有新的支付算法,只需要实现支付接口,扩展支付的实现就行
-
避免使用了多条件的if,else,增加了可读性
-
提高内部算法的保密性和安全性
缺点
- 客户端调用时必须知道所有的策略,并且自行决定使用哪一个策略
- 代码中会产生非常多策略类,增加维护难度。