咱們都知道,設計模式(Design Pattern)是前輩們對代碼開發經驗的總結,是解決特定問題的一系列套路。它不是語法規定,而是一套用來提升代碼可複用性、可維護性、可讀性、穩健性以及安全性的解決方案。
那麼,咱們可能都瞭解過設計模式,可是在項目中怎麼使用可能仍是會有點疑惑,今天,公司的項目恰好有一個場景來讓我使用一個設計模式:策略模式。前端
關於用戶訂單充值(訂單支付同理),咱們都知道,現今的支付方式是很是的多的,例如:支付寶、微信、銀聯、錢包(各個APP的帳戶餘額)等等。
查詢實體Query:java
/** * @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的代碼將變成啥樣。app
/** * @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的代碼了。ide
下面進行詳細的講解:
一、首先,咱們須要自定義一個註解,來標識一個支付類型對應的一個處理器。微信支付
/** * @author Howinfun * @desc 自定義註解,標識支付類型 * @date 2019/11/2 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface PayWayHandler { String value(); }
二、接着,抽象一個處理器,讓每一個支付方式的處理器繼承此抽象處理器,實現handle方法:this
/** * @author Howinfun * @desc 抽象訂單類型處理器 * @date 2019/11/2 */ public abstract class AbstractPayWayHandler { /** * * @param query * @return */ abstract public Result handler(ChargeQuery query); }
三、實現類,例如支付寶、微信、銀聯:
注意:每一個處理器都要加上@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; } }
四、而後最重點的來了,建立一個類,實現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 代碼,而且擴展性獲得了大大的提高,再也不擔憂由於支付方式的新增而修改業務代碼。