直入主题

我们都知道,设计模式(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!的更多相关文章

  1. 如何在Spring Boot项目中集成微信支付V3

    Payment Spring Boot 是微信支付V3的Java实现,仅仅依赖Spring内置的一些类库.配置简单方便,可以让开发者快速为Spring Boot应用接入微信支付. 演示例子: paym ...

  2. spring boot学习02【如何在spring boot项目中访问jsp】

    1.配置application.properties文件 打开application.properties追加 spring.mvc.view.prefix=/WEB-ROOT/ spring.mvc ...

  3. 在Spring Boot项目中使用Spock框架

    转载:https://www.jianshu.com/p/f1e354d382cd Spock框架是基于Groovy语言的测试框架,Groovy与Java具备良好的互操作性,因此可以在Spring B ...

  4. 在Spring Boot项目中使用Spock测试框架

    本文首发于个人网站:在Spring Boot项目中使用Spock测试框架 Spock框架是基于Groovy语言的测试框架,Groovy与Java具备良好的互操作性,因此可以在Spring Boot项目 ...

  5. Spring Boot项目中如何定制拦截器

    本文首发于个人网站:Spring Boot项目中如何定制拦截器 Servlet 过滤器属于Servlet API,和Spring关系不大.除了使用过滤器包装web请求,Spring MVC还提供Han ...

  6. 你真的理解 Spring Boot 项目中的 parent 吗?

    前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId ...

  7. Spring Boot项目中使用Swagger2

    Swagger2是一款restful接口文档在线生成和在线接口调试工具,Swagger2在Swagger1.x版本的基础上做了些改进,下面是在一个Spring Boot项目中引入Swagger2的简要 ...

  8. Spring Boot2 系列教程(三)理解 Spring Boot 项目中的 parent

    前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId ...

  9. Spring Boot项目中使用Mockito

    本文首发于个人网站:Spring Boot项目中使用Mockito Spring Boot可以和大部分流行的测试框架协同工作:通过Spring JUnit创建单元测试:生成测试数据初始化数据库用于测试 ...

随机推荐

  1. java异常类的妙用

    异常类的妙用   以往在使用异常时,只是知道通过异常类的构造方法设置一些出错信息,此外最多就是把引起该异常的原因通过Throwable类的子类一同设置进去.今天在分析springSecurity3.0 ...

  2. Spring 梳理-Spring配置文件 -<context:annotation-config/>和<context:component-scan base-package=""/>和<mvc:annotation-driven /> 的区别

    <context:annotation-config/> 在基于主机方式配置Spring时,Spring配置文件applicationContext.xml,你可能会见<contex ...

  3. sklearn 标准化数据的方法

    Sklearn 标准化数据 from __future__ import print_function from sklearn import preprocessing import numpy a ...

  4. 2019 中国.NET 开发者峰会正式启动

    2014年微软组织并成立.NET基金会,微软在成为主要的开源参与者的道路上又前进了一步.2014年以来已经有众多知名公司加入.NET基金会,Google,微软,AWS三大云厂商已经齐聚.NET基金会, ...

  5. 如何写md格式的文档

    一.标题 标题其实和HTML中的h系列很像,想要设置为标题的文字前面加#来表示一个#是一级标题,二个#是二级标题,以此类推.支持六级标题. 注:标准语法一般在#后跟个空格再写文字, 示例: 效果如下: ...

  6. Windows 10 删除C盘的用户,恢复技巧

    起因 入域不成功,然后强制删除了C盘 Users里面 账户 然后再进行入域的时候就入域不成功了,如果再进行登录就会提示 无法登录到你的账户 通常可以通过从你的账户注销,然后重新登录解决此问题 如果不立 ...

  7. ES6——新增数据结构Set与Map的用法

    ES6 提供了新的数据结构 Set以及Map,下面我们来一一讲解. 一.Set 特性 似于数组,但它的一大特性就是所有元素都是唯一的,没有重复. 我们可以利用这一唯一特性进行数组的去重工作. 1.单一 ...

  8. Python数据分析入门与实践 ✌✌

    Python数据分析入门与实践 (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 这是一个数据驱动的时代,想要从事机器学习.人工智能.数据挖掘等前沿技术,都离不开 ...

  9. git基础命令详解

    一些必须要知道的概念 git的三个工作区域:工作目录.暂存区.git仓库. 工作目录:其实就是本地文件磁盘上的文件或目录: 暂存区:是一个文件,保存了下次提交的文件列表信息,一般在git仓库目录中: ...

  10. 零基础:如何快速学习JavaScript,html+css技术

    前端开发要学的知识内容涉及的会很宽泛,虽然说主要是HTML.CSS和JavaScript这些基础知识点,但达妹今天想强调一下,学前端开发除了要学这些基础知识外,学员还要在这之上进行延伸和深入的去学,而 ...