前言
人間清醒
- 前言
- 聚合支付歷史版本代碼
- 重構版本聚合支付代碼
- 定義支付統一引數DTO
- 定義支付行為
- 定義支付方式注解
- 定義支付具體邏輯Bean保存容器
- PayService實作注冊PayBeanContainer容器中
- 定義支付模板方法
- 開發微信支付
- 提供統一支付介面給前端呼叫
- 呼叫統一支付介面測驗微信支付
- 擴展支付寶支付
- 呼叫統一支付介面測驗支付寶支付
- 總結
- 今天你學費了嗎?
聚合支付歷史版本代碼
以下代碼邏輯為:按照不同的支付方式呼叫不同支付方式的邏輯流程,
痛點:
- 增加一種支付方式就要加入一個case,違反了開閉原則
- 代碼累計在一個類中榷訓月累越來越沉重,可讀性極差
- 增加一種支付方式就需要在原來的代碼上動刀,擴展性極低
/**
* 舊的支付
*
* @param mode 模式
* @param payVO 支付簽證官
* @return {@link String}
*/
@PostMapping("/old/{mode}")
public String oldPay(@PathVariable("mode") String mode, @RequestBody PayVO payVO) {
switch (mode) {
case "ALIPAY":
// 呼叫支付寶支付流程邏輯方法
break;
case "WXPAY":
// 呼叫微信支付流程邏輯方法
break;
case "EBANK":
// 呼叫E支付流程邏輯方法
break;
default:
return "支付方式未找到!";
}
return "支付成功!";
}
看邏輯和你寫的代碼八九不離十吧?
當時你的想法可能是“能實作這個需求就行,擴展性是啥?可維護性是啥?關我鳥事”,于是劈里啪啦一千又一千行造就了一個萬行類誕生,根據需求的變更,需求的迭代慢慢的發現自己都改不動萬行類,這時候咋辦?(-v-,程式和人一個能跑就行-,-);
重構版本聚合支付代碼
考慮到大部分專案使用了Spring,那咋們今天就用Spring的特性來實作這次重構的需求,
定義支付統一引數DTO
這里DTO定義兩個引數:
- mode: 支付方式,如:支付寶,微信等等;
- T: 泛型,定義為泛型主要考慮到不同第三方平臺的支付介面引數不一樣,保證比較好的適配,
/**
* 支付dto
*
* @author wentao.wu
* @date 2022/01/05
*/
@Data
public class PayDTO<T> implements Serializable {
/**
* 模式
*/
private String mode;
/**
* 資料
*/
private T data;
}
定義支付行為
這里定義好一個支付行為需要做的事情,方法作用解讀:
- befor: 之前需要執行的操作寫著這里面,比如需要進行payDTO引數轉換,校驗支付行為是否合法等等,
- invoke: 執行具體的支付邏輯,比如呼叫微信支付介面進行支付,
- errorAfter:invoke方法執行失敗后呼叫,比如支付失敗記錄失敗日志等,
- okAfter:invoke方法執行成功后呼叫,比如支付成功后發送訊息通知通知用戶支付成功等,
/**
* 支付服務
*
* @author wentao.wu
* @date 2022/01/04
*/
public interface PayService {
/**
* 支付之前
*
* @param payDTO 支付dto
*/
<T> void befor(PayDTO<T> payDTO);
/**
* 執行支付
*
* @param payDTO 支付dto
* @return boolean
*/
<T> boolean invoke(PayDTO<T> payDTO);
/**
* 支付失敗后
*
* @param payDTO 支付dto
*/
<T> void errorAfter(PayDTO<T> payDTO);
/**
* 支付成功后
*
* @param payDTO 支付dto
*/
<T> void okAfter(PayDTO<T> payDTO);
}
定義支付方式注解
這里定義注解主要是宣告PayService實作類對應了什么方式,用來標注在具體的支付方式實作類中,
/**
* 支付
*
* @author wentao.wu
* @date 2022/01/05
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Pay {
/**
* 模式: WXPAY ,ALIPAY.....
*
* @return {@link String}
*/
String mode();
}
定義支付具體邏輯Bean保存容器
這里主要是保存所有PayService的實作類,后續會通過前端引數傳入的支付方式從容器中尋找系統是否支持該支付方式,
/**
* 支付bean容器
*
* @author wentao.wu
* @date 2022/01/05
*/
public class PayBeanContainer {
/**
* bean管理器
*/
public Map<String, PayService> CONTAINER = new HashMap<>(16);
/**
* 添加bean
*
* @param mode 支付方式
* @param bean Bean
*/
public void addBean(String mode, PayService bean) {
if (checkBean(mode)) {
throw new RuntimeException("已存在該業務規則!");
}
CONTAINER.put(mode, bean);
}
/**
* 獲取Bean
*
* @param mode 支付方式
* @return {@link PayService}
*/
public PayService getBean(String mode) {
if (!checkBean(mode)) {
throw new RuntimeException("不存在該Bean!");
}
return CONTAINER.get(mode);
}
/**
* 檢查Bean是否存在
*
* @param mode 支付方式
* @return boolean
*/
public boolean checkBean(String mode) {
return CONTAINER.containsKey(mode);
}
}
PayService實作注冊PayBeanContainer容器中
這里通過Spring初始化Bean后將所有PayService的實作類注冊到支付容器中,支付方式則對應Pay注解中的mode,
/**
* 支付bean注冊表
*
* @author wentao.wu
* @date 2022/01/04
*/
@Slf4j
@Component
public class PayServiceInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Autowired
private PayBeanContainer payBeanContainer;
/**
* 發布程序實體化后
*
* @param bean 豆
* @param beanName bean的名字
* @return boolean
* @throws BeansException 豆子例外
*/
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if (bean instanceof PayService) {
Pay annotation = bean.getClass().getAnnotation(Pay.class);
payBeanContainer.addBean(annotation.mode(), (PayService) bean);
log.info(" PayBeanContainer.addBean:{}", bean.getClass().getName());
}
return true;
}
}
定義支付模板方法
統一支付流程的規范,并且實作呼叫的流程,具體子流程由開發自己實作,
當前模板方法主要統一了支付呼叫流程:
- 判斷支付方式是否存在,存在則向下走,
- 從容器獲取該支付方式的具體邏輯實作類,
- 呼叫支付前置方法,
- 執行支付,
- 支付成功,呼叫支付成功回呼方法,
- 支付失敗,呼叫支付失敗回呼方法,
這里定義了大概流程,具體微信支付,支付前,支付,支付后回呼需要處理什么邏輯由開發自行實作,
/**
* 支付模板
*
* @author wentao.wu
* @date 2022/01/04
*/
@Component
public class PayTemplate {
@Autowired
private PayBeanContainer payBeanContainer;
/**
* 支付
*
* @param payDTO 支付dto
* @return boolean
*/
public <T> boolean pay(PayDTO<T> payDTO) {
if (!payBeanContainer.checkBean(payDTO.getMode())) {
throw new RuntimeException("平臺不支持該支付方式,非法呼叫!");
}
PayService server = payBeanContainer.getBean(payDTO.getMode());
server.befor(payDTO);
boolean result = server.invoke(payDTO);
if (result) {
server.okAfter(payDTO);
} else {
server.errorAfter(payDTO);
}
return result;
}
}
開發微信支付
經過我們上面的鋪墊開發實作微信支付只需要統一實作PayService并且宣告這個子類為微信支付的具體呼叫邏輯(@Pay(mode = "WXPAY"))
/**
* wxpay impl
*
* @author wentao.wu
* @date 2022/01/06
*/
@Component
@Slf4j
@Pay(mode = "WXPAY")
public class WXPayImpl implements PayService {
@Override
public <T> void befor(PayDTO<T> payDTO) {
log.info("微信支付前,準備引數等....");
}
@Override
public <T> boolean invoke(PayDTO<T> payDTO) {
log.info("呼叫微信支付介面提交支付....");
return true;
}
@Override
public <T> void errorAfter(PayDTO<T> payDTO) {
log.info("微信支付失敗記錄日志....");
}
@Override
public <T> void okAfter(PayDTO<T> payDTO) {
log.info("微信支付成功給用戶發送訊息通知用戶支付成功了....");
}
}
提供統一支付介面給前端呼叫
創建前端引數VO:
/**
* 支付VO
*
* @author wentao.wu
* @date 2022/01/05
*/
@Data
public class PayVO implements Serializable {
private String param1;// xx支付需要的引數
}
提供介面給前端呼叫:
@RestController
@RequestMapping("/pay")
public class PayController {
@Autowired
private PayTemplate payTemplate;
/**
* 支付
*
* @param mode 模式
* @param payVO 支付簽證官
* @return {@link String}
*/
@PostMapping("/{mode}")
public String pay(@PathVariable("mode") String mode, @RequestBody PayVO payVO) {
PayDTO<PayVO> payDTO = new PayDTO<>();
payDTO.setMode(mode);
boolean reslult = payTemplate.pay(payDTO);
if (reslult) {
return "支付成功!";
}
return "支付失敗!";
}
/**
* 舊的支付
*
* @param mode 模式
* @param payVO 支付簽證官
* @return {@link String}
*/
@PostMapping("/old/{mode}")
public String oldPay(@PathVariable("mode") String mode, @RequestBody PayVO payVO) {
switch (mode) {
case "ALIPAY":
// 呼叫支付寶支付流程邏輯方法
break;
case "WXPAY":
// 呼叫微信支付流程邏輯方法
break;
case "EBANK":
// 呼叫E支付流程邏輯方法
break;
default:
return "支付方式未找到!";
}
return "支付成功!";
}
}
呼叫統一支付介面測驗微信支付
Postman呼叫:http://localhost:7776/pay/WXPAY
RequestBody引數為:{"param1":"引數1"}
呼叫結果:支付成功!
控制臺列印日志:
2022-01-06 12:45:37.441 INFO 15276 --- [nio-7776-exec-3] c.g.b.g.service.impl.WXPayImpl : 微信支付前,準備引數等....
2022-01-06 12:45:37.441 INFO 15276 --- [nio-7776-exec-3] c.g.b.g.service.impl.WXPayImpl : 呼叫微信支付介面提交支付....
2022-01-06 12:45:37.442 INFO 15276 --- [nio-7776-exec-3] c.g.b.g.service.impl.WXPayImpl : 微信支付成功給用戶發送訊息通知用戶支付成功了....
擴展支付寶支付
這里我們需要擴展一種新的支付方式不需要改動任何代碼,遵守開閉原則,每個類單一職責,
/**
* 支付寶impl
*
* @author wentao.wu
* @date 2022/01/06
*/
@Component
@Slf4j
@Pay(mode = "ALIPAY")
public class AlipayImpl implements PayService {
@Override
public <T> void befor(PayDTO<T> payDTO) {
log.info("支付寶支付前,準備引數等....");
}
@Override
public <T> boolean invoke(PayDTO<T> payDTO) {
log.info("呼叫支付寶支付介面提交支付....");
return true;
}
@Override
public <T> void errorAfter(PayDTO<T> payDTO) {
log.info("支付寶支付失敗記錄日志....");
}
@Override
public <T> void okAfter(PayDTO<T> payDTO) {
log.info("支付寶支付成功給用戶發送訊息通知用戶支付成功了....");
}
}
呼叫統一支付介面測驗支付寶支付
Postman呼叫:http://localhost:7776/pay/ALIPAY
RequestBody引數為:{"param1":"引數1"}
呼叫結果:支付成功!
控制臺列印日志:
2022-01-06 13:49:50.141 INFO 13656 --- [nio-7776-exec-2] c.g.b.g.service.impl.AlipayImpl : 支付寶支付前,準備引數等....
2022-01-06 13:49:50.141 INFO 13656 --- [nio-7776-exec-2] c.g.b.g.service.impl.AlipayImpl : 呼叫支付寶支付介面提交支付....
2022-01-06 13:49:50.141 INFO 13656 --- [nio-7776-exec-2] c.g.b.g.service.impl.AlipayImpl : 支付寶支付成功給用戶發送訊息通知用戶支付成功了....
總結
源代碼地址: https://gitee.com/SimpleWu/blogs-examples/tree/master/grace-pay-case
- 經過重構后代碼可讀性變得強了起來,需要修改微信支付看WXPayImpl,需要修改支付寶支付看AlipayImpl,讓代碼變得清晰,
- 經過重構后代碼擴展性變得強了起來,需要擴展只需要繼承PayService并且宣告@Pay支付方式即可,
- 使用了模板方法模式,組成了支付流程骨架,將步驟延遲到了依賴類去實作,
- 使用了策略模式將所有支付方式統一保存管理了起來,當需要某種策略時則去向策略管理器要,
- ......
今天你學費了嗎?
-.-
學習是永無止境的,轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/404263.html
標籤:Java
