Java策略模式,与根据SpringBoot的监听在项目中实现应用
这段时间在公司接了一个新的需求,用策略模式来重构我们的支付模块,在该模块中有多个需要对接的支付接口,而支付接口需要的参数都是差不多的,之前的写的时候为了图方便,都是用的大量的判断调用具体哪个支付接口,渐渐的就造成了代码过于难看,维护困难,需要进行重构
策略模式是什么?
策略模式主要就是用来解决大量的判断场景下,用接口和其实现类来进行简化模块,达到去除if的目的,使代码更容易维护,不过前提是需要确定接口需要实现的方法有哪些,实现的方法中的参数是什么,不然如果接口没有设计好的话,之后的一点改动都会影响所有实现该接口的类,在使用该模式时,必须要想好这两个问题。
策略模式的应用场景
用于大量判断来执行一些操作很类似的,可以根据某些特征来区分的行为。
就比如上面说的,支付模块,支付模块里主要就是几个主要方法,下单,订单查询,退款,退款查询,支付回调接口。而且这几个接口的参数大致相同,因此可以用策略模式完美解决大量if的问题。
使用策略模式和不使用策略模式的代码区别
这是没有使用策略模式的支付下单接口:
//判断支付方式
switch (type){
//支付 1
case 0:
return pay1.order();
//支付2
case 1:
return pay2.order();
case 2:
return pay3.order();
case 3:
return pay4.order();
default:
return "请检查是否配置了支付方式";
}
如果再对接一个支付,那下单,退款等方法是不是又要增加一个case?这还是没有增加任何操作的下单,如果加入了其他操作的话那代码可读性那肯定更差了。维护起来也会越来越麻烦。
这是使用策略模式的支付下单接口:
IPayImpl payObj=payServiceContext.get(type);
if(payObj==null){
return "未配置支付方式。";
}
return payObj.order();
这对比起来是不是看起来就简洁多了呢?再对接接口只要添加一个实现类就好了,调用类根本不需要再做其他的改变。
下面是策略模式的实现
首先先创建一个接口,所有对接的支付方法都要实现该接口
package com.lin.pay.service.impl;
/**
* 支付接口的接口类
* @Author: lin
*/
public interface IPayImpl {
/**
* 下单
* @return 返回下单结果
* @throws Exception 类型装换或解密时的异常
*/
String wxPreCreate()throws Exception;
/**
* 订单查询
* @return 查询返回结果
* @throws Exception 类型装换或解密时的异常
*/
String commonQuery() throws Exception;
/**
* 退款
* @return 退款返回结果
* @throws Exception 类型装换或解密时的异常
*/
String commonRefund() throws Exception;
/**
* 退款查询
* @return 退款查询返回结果
* @throws Exception 类型装换或解密时的异常
*/
String refundQuery() throws Exception;
/**
* 异步通知
* @return
* @throws Exception
*/
String notify() throws Exception;
}
接下来创建一个实现类
/**
* @Author: lin
*/
@Service
@Slf4j
//实现类必须使用的自定义注解,来判断支付类型
@PayTypeAnnotation(PayTypeEnum.PAY0)
public class FyIPayService implements IPayImpl {
@Override
String wxPreCreate()throws Exception{
return "";
}
@Override
String commonQuery() throws Exception{
return "";
}
@Override
String commonRefund() throws Exception{
return "";
}
@Override
String refundQuery() throws Exception{
return "";
}
@Override
String notify() throws Exception{
return "";
}
}
该类实现支付接口后,所有的逻辑都在实现类里写,再使用注解来标识他的支付类型,在下面的springboot监听里会写到。
创建一个自定义注解,来区分支付类型
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 支付接口注解,如果新增了支付接口就必须要更新该注解,并引用到支付接口的实现类
* @Author: lin
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PayTypeAnnotation {
PayTypeEnum value();
}
注解里使用的枚举类:
/**
* @Author: lin
*/
public enum PayTypeEnum {
/**
* 支付类型
*/
PAY0(0,"支付1"),
PAY1(1,"支付2");
private Integer type;
private String name;
PayTypeEnum(Integer type,String name){
this.type=type;
this.name=name;
}
public Integer getType(){
return type;
}
public String getName() {
return name;
}
}
不用注解也行,可以在接口类里增加一个type字段,让实现类强制实现这个字段,根据字段进行区分,不过这个方法虽然简单点,但是不推荐使用,使用注解的话代码易读性会更好。
使用springboot的监听机制来进行存贮各种支付类型
首先创建一个map,来存贮接口的实现类
/**
* 支付服务map
* 类中维护了一个Map,用来存放IPayImpl的实现类,然后通过@Component交由Spring容器管理
* @Author: lin
*/
@Component
public class PayServiceContext {
/**
* 用来存储支付类型如:聚合,富友支付的实现类
*/
private final static Map<Integer, IPayImpl> PAY_MAP;
static {
PAY_MAP=new HashMap<>();
}
public IPayImpl get(Integer type){
return PAY_MAP.get(type);
}
public void put(Integer type, IPayImpl payObj){
PAY_MAP.put(type,payObj);
}
接下来创建一个springboot的监听,在启动时将实现类新增进去
/** 定义Springboot的监听器
* 项目启动后用来处理自定义支付接口,先拿到每个添加了自定义注解的类,然后通过反射拿到该类的自定义注解,
* 再通过自定义注解中的值(支付类型枚举),然后把该值添加进中Map中
* @Author: lin
*/
@Component
@Slf4j
public class PayTypeListener implements ApplicationListener<ContextRefreshedEvent> {
@Resource
private PayServiceContext payServiceContext;
@Override
public void onApplicationEvent(@NotNull ContextRefreshedEvent event) {
Map<String, IPayImpl> beans = event.getApplicationContext().getBeansOfType(
IPayImpl.class);
log.info("正在获取支付接口实现类");
beans.forEach((k, calcService) -> {
Class clazz = calcService.getClass();
PayTypeAnnotation payTypeListener = (PayTypeAnnotation)clazz.getAnnotation(PayTypeAnnotation.class);
payServiceContext.put(payTypeListener.value().getType(), calcService);
});
log.info("支付接口实现类获取完成");
}
大功告成!接下来只需要简单的调用就好了,调用方式为:
@Service
@Slf4j
public class test{
@Autowired
private PayServiceContext payServiceContext;
public String test(Integer type){
IPayImpl payObj=payServiceContext.get(payType);
if(payObj==null){
return "未配置支付方式。";
}
return payObj.wxPreCreate();
}
}
最后总结,使用设计模式来优化代码是非常棒的,如果一个项目使用了很多的设计模式,那维护起来也非常轻松,就不会出现那种,不敢碰的祖传代码了哈哈哈哈哈
本文参考资料:https://blog.csdn.net/lovexiaotaozi/article/details/103910776#comments