直入主題
我們都知道,設計模式(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 代碼,並且擴展性得到了大大的提升,不再擔心因為支付方式的新增而修改業務代碼。
