如何在Spring Boot项目中巧妙利用策略模式干掉if else!
直入主题
我们都知道,设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
那么,我们可能都了解过设计模式,但是在项目中怎么使用可能还是会有点疑惑,今天,公司的项目刚好有一个场景来让我使用一个设计模式:策略模式。
场景
关于用户订单充值(订单支付同理),我们都知道,现今的支付方式是非常的多的,例如:支付宝、微信、银联、钱包(各个APP的账户余额)等等。
查询实体Query:
/**
* @author Howinfun
* @desc 查询
* @date 2019/10/22
*/
@Data
public class ChargeQuery {
/** 支付方式(ALI/WX/UNION) */
@NotBlank(message = "支付方式不能为空",groups = PayWayNotBlank.class)
private String payWay;
/** 充值金额 */
@NotNull(message = "充值金额不能为空",groups = AmountNotNull.class)
private Double amount;
}
Service接口:
/**
* @author Howinfun
* @desc 充电-充值模块
* @date 2019/10/30
*/
public interface ChargeRechargeService {
/**
* 根据不用支付方式进行用户余额充值
* @param query
* @return
*/
Result recharge(ChargeQuery query);
/**
* 充值回调
* @param rechargeCallBack
*/
Result rechargeCallBack(RechargeCallBack rechargeCallBack);
}
传统方式实现
就是利用if else或者switch来进行条件判断:
/**
* @author Howinfun
* @desc
* @date 2019/10/30
*/
@Service
@AllArgsConstructor
@Slf4j
public class ChargeRechargeServiceImpl implements ChargeRechargeService {
private final CarUserMapper carUserMapper;
private final IncomePaymentMapper incomePaymentMapper;
private final RechargeRecordMapper rechargeRecordMapper;
private final PayWayHandlerContext payWayHandlerContext;
@Override
@Transactional(rollbackFor = Exception.class)
public Result recharge(ChargeQuery query) {
Result result = new Result();
// ......
// ......
if (PayConstant.PAY_WAY_WX.equals(query.getPayWay())){
// 微信
// ......
}else if (PayConstant.PAY_WAY_ALI.equals(query.getPayWay())){
// 支付宝
// ......
}else if (PayConstant.PAY_WAY_UNION_PAY.equals(query.getPayWay())){
// 银联
// ......
}
return result;
}
}
总结:我们可以看到,传统的实现方式是非常的笨重的,而且代码非常的不简洁,扩展性差。假如我们要接入新的支付方式,那么我们只能继续添加 else if。
策略模式
Talk is cheap,show me the code.
我们先看一下,如果使用策略模式,service的代码将变成啥样。
/**
* @author Howinfun
* @desc
* @date 2019/10/30
*/
@Service
@AllArgsConstructor
@Slf4j
public class ChargeRechargeServiceImpl implements ChargeRechargeService {
private final PayWayHandlerContext payWayHandlerContext;
@Override
@Transactional(rollbackFor = Exception.class)
public Result recharge(ChargeQuery query) {
return this.payWayHandlerContext.getHandlerInstance(query.getPayWay()).handler(query);
}
}
emmmm,确实是简单了不少。不但代码量少了,简洁了,而且不再担心因为新增支付方式而修改serviceImpl的代码了。
下面进行详细的讲解:
1、首先,我们需要自定义一个注解,来标识一个支付类型对应的一个处理器。
/**
* @author Howinfun
* @desc 自定义注解,标识支付类型
* @date 2019/11/2
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface PayWayHandler {
String value();
}
2、接着,抽象一个处理器,让每个支付方式的处理器继承此抽象处理器,实现handle方法:
/**
* @author Howinfun
* @desc 抽象订单类型处理器
* @date 2019/11/2
*/
public abstract class AbstractPayWayHandler {
/**
*
* @param query
* @return
*/
abstract public Result handler(ChargeQuery query);
}
3、实现类,例如支付宝、微信、银联:
注意:每个处理器都要加上@component,交给Spring管理。
/**
* @author Howinfun
* @desc 支付宝支付
* @date 2019/11/2
*/
@Component
@PayWayHandler("ALI")
@Slf4j
@AllArgsConstructor
public class AliPayWayHandler extends AbstractPayWayHandler {
// ....各种依赖
@Override
public Result handler(ChargeQuery query) {
Result result = new Result();
// ......
return result;
}
}
/**
* @author Howinfun
* @desc 微信支付
* @date 2019/11/2
*/
@Component
@PayWayHandler("WX")
@Slf4j
@AllArgsConstructor
public class WxPayWayHandler extends AbstractPayWayHandler {
// ....各种依赖
@Override
public Result handler(ChargeQuery query) {
Result result = new Result();
// ......
return result;
}
}
/**
* @author Howinfun
* @desc 银联支付
* @date 2019/11/2
*/
@Component
@PayWayHandler("UNION")
@Slf4j
@AllArgsConstructor
public class UnionPayWayHandler extends AbstractPayWayHandler {
// ....各种依赖
@Override
public Result handler(ChargeQuery query) {
Result result = new Result();
// ......
return result;
}
}
4、然后最重点的来了,创建一个类,实现ApplicationContextAware接口,重写setApplicationContext方法,然后扫描带有自定义注解@PayWayHandler的Bean,然后存储起来,方便Service的获取。
/**
* @author Howinfun
* @desc
* @date 2019/11/2
*/
@Component
public class PayWayHandlerContext implements ApplicationContextAware {
@Autowired ApplicationContext applicationContext;
/** key为PayWay,value为class*/
private static final Map<String,Class> handlerMap = new HashMap<>(10);
public AbstractPayWayHandler getHandlerInstance(String payType){
Class clazz = handlerMap.get(payType);
if (clazz == null){
throw new CustomDeniedException("暂不支持此支付方式");
}
return (AbstractPayWayHandler) applicationContext.getBean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 遍历带有PayTypeHandler注释的类
Map<String,Object> beans = applicationContext.getBeansWithAnnotation(PayWayHandler.class);
if (beans != null && beans.size() > 0) {
for (Object serviceBean : beans.values()) {
String payType = serviceBean.getClass().getAnnotation(PayWayHandler.class).value();
handlerMap.put(payType, serviceBean.getClass());
}
}
}
}
总结:到此,ServiceImpl可根据前端传过来的payWay来选择对应的handler来处理。我利用了策略模式简化了繁杂的 if else 代码,并且扩展性得到了大大的提升,不再担心因为支付方式的新增而修改业务代码。
如何在Spring Boot项目中巧妙利用策略模式干掉if else!的更多相关文章
- 如何在Spring Boot项目中集成微信支付V3
Payment Spring Boot 是微信支付V3的Java实现,仅仅依赖Spring内置的一些类库.配置简单方便,可以让开发者快速为Spring Boot应用接入微信支付. 演示例子: paym ...
- spring boot学习02【如何在spring boot项目中访问jsp】
1.配置application.properties文件 打开application.properties追加 spring.mvc.view.prefix=/WEB-ROOT/ spring.mvc ...
- 在Spring Boot项目中使用Spock框架
转载:https://www.jianshu.com/p/f1e354d382cd Spock框架是基于Groovy语言的测试框架,Groovy与Java具备良好的互操作性,因此可以在Spring B ...
- 在Spring Boot项目中使用Spock测试框架
本文首发于个人网站:在Spring Boot项目中使用Spock测试框架 Spock框架是基于Groovy语言的测试框架,Groovy与Java具备良好的互操作性,因此可以在Spring Boot项目 ...
- Spring Boot项目中如何定制拦截器
本文首发于个人网站:Spring Boot项目中如何定制拦截器 Servlet 过滤器属于Servlet API,和Spring关系不大.除了使用过滤器包装web请求,Spring MVC还提供Han ...
- 你真的理解 Spring Boot 项目中的 parent 吗?
前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId ...
- Spring Boot项目中使用Swagger2
Swagger2是一款restful接口文档在线生成和在线接口调试工具,Swagger2在Swagger1.x版本的基础上做了些改进,下面是在一个Spring Boot项目中引入Swagger2的简要 ...
- Spring Boot2 系列教程(三)理解 Spring Boot 项目中的 parent
前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId ...
- Spring Boot项目中使用Mockito
本文首发于个人网站:Spring Boot项目中使用Mockito Spring Boot可以和大部分流行的测试框架协同工作:通过Spring JUnit创建单元测试:生成测试数据初始化数据库用于测试 ...
随机推荐
- 23种设计模式之装饰器模式(Decorator Pattern)
装饰器模式(Decorator Pattern) 允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包 ...
- C#刷遍Leetcode面试题系列连载(1) - 入门与工具简介
目录 为什么要刷LeetCode 刷LeetCode有哪些好处? LeetCode vs 传统的 OJ LeetCode刷题时的心态建设 C#如何刷遍LeetCode 选项1: VS本地Debug + ...
- SUSE Ceph 快速部署 - Storage6
学习 SUSE Storage 系列文章 (1)SUSE Storage6 实验环境搭建详细步骤 - Win10 + VMware WorkStation (2)SUSE Linux Enterpri ...
- Flume 知识点(六)Flume 的监控
简述 使用 Flume 实时收集日志的过程中,尽管有事务机制保证数据不丢失,但仍然需要时刻关注 Source.Channel.Sink 之间的消息传输是否正常. 比如,SouceChannel 传输了 ...
- Android中Project、Module的区别
Project 可以包含多含 Module. Project相当于eclipse里面的工作区间,module相当于其project.module可以作为狭义上的模块,可以多个app共用的module. ...
- spring源码分析系列4:ApplicationContext研究
ApplicationContext接口 首先看一下一个最基本的上下文应该是什么样子 ApplicationContext接口的注释里写的很清楚: 一个基本applicationContext应该提供 ...
- windows下将jar文件设置为系统服务
jar文件的执行需要java环境,怎么配置环境相信不用说了 因为不想每次开机都手动启动一次程序,那么我们就需要把它配置成开机自启动的服务,下面就来讲一种方法 首先,我们知道jar文件的执行命令为 ja ...
- 从零开始入门 K8s | 可观测性:监控与日志
作者 | 莫源 阿里巴巴技术专家 一.背景 监控和日志是大型分布式系统的重要基础设施,监控可以帮助开发者查看系统的运行状态,而日志可以协助问题的排查和诊断. 在 Kubernetes 中,监控和日志 ...
- e课表项目第二次冲刺周期第八天
昨天完成了什么? 昨天,我们组商量讨论了二层界面的设计,添加课程所需要的信息大概有:课程名称.教室.任课教师.上课时间.类型(单周.双周.单双周)以及备注等等.然后,我们通过界面的UI设计,让我们软件 ...
- BZOJ 1345: [Baltic2007]序列问题Sequence
1345: [Baltic2007]序列问题Sequence Time Limit: 5 Sec Memory Limit: 162 MBSubmit: 1180 Solved: 633[Subm ...