首先簡述一個場景昼牛,訂單的狀態(tài)流轉(zhuǎn)术瓮。 一個訂單會有很多種狀態(tài):臨時單、已下單贰健、待支付胞四、已支付、已完成伶椿、退款中等等辜伟。每一種狀態(tài)都和其扭轉(zhuǎn)前的狀態(tài)、在扭轉(zhuǎn)前狀態(tài)所執(zhí)行的操作有關(guān)悬垃。
一 引子
舉例一個過程:用戶將商品加入購物車游昼,在后臺生成了一個所謂的“臨時單”,這個訂單實際上還沒有正式生成尝蠕,因為用戶仍然沒有點擊下單。只有當(dāng)用戶下單后载庭,這個“臨時單”才可以轉(zhuǎn)化為一個“待支付的訂單”看彼。那么這個過程中:只有將一個處于“臨時單”狀態(tài)的訂單執(zhí)行下單操作,才能得到一個狀態(tài)為“待支付”的訂單囚聚。 即--一個前置狀態(tài)+一個恰當(dāng)?shù)牟僮骶搁牛拍芘まD(zhuǎn)訂單的狀態(tài)。在這個過程中顽铸,如果是硬編碼茁计,那么我們需要一系列的 if...else 語句來檢查訂單的當(dāng)前狀態(tài)、可執(zhí)行操作以及這兩個的組合得到的下一個應(yīng)該被流轉(zhuǎn)的狀態(tài)值谓松。如果訂單的狀態(tài)流轉(zhuǎn)很復(fù)雜的話星压,寫出來的邏輯就會很復(fù)雜,并且可讀性很低鬼譬。后期的維護就是一個坑娜膘。
二 狀態(tài)設(shè)計模式與訂單狀態(tài)流轉(zhuǎn)
處理這個問題,我們可以使用 狀態(tài)機設(shè)計模式 來處理优质。對應(yīng)到實踐竣贪,就是狀態(tài)機。
關(guān)于狀態(tài)機設(shè)計模式的具體內(nèi)容巩螃,可以自行百度演怎。這里用簡單的一句話來概括的話:對象的內(nèi)部狀態(tài)隨外部執(zhí)行條件的變化而變化。再映射到訂單狀態(tài)的流轉(zhuǎn)上:訂單的狀態(tài)避乏,隨訂單當(dāng)前狀態(tài)和目前執(zhí)行操作的組合而變化爷耀。
三 編碼前的抽象與設(shè)計
圖示模擬一個訂單狀態(tài)的流轉(zhuǎn)流程。從一個臨時訂單開始淑际,每當(dāng)訂單處于某一個已知的狀態(tài)的時候畏纲,要想讓這個訂單改變狀態(tài)扇住,就需要我們?nèi)?zhí)行對應(yīng)的操作。
從狀態(tài)機角度來說盗胀,我們先將各種信息進行抽象和處理
3.1 代碼抽象
編寫對應(yīng)訂單狀態(tài)枚舉類
public enum OrderStatusEnum {
CREATE_EVENT(1, "創(chuàng)建訂單"),
FORMAL_EVENT(2, "正式訂單"),
NEED_PAY(3, "待支付"),
PAY_DONE(4, "已支付"),
ORDER_FINISHED(5, "訂單已完成"),
ORDER_CANCEL(6, "訂單已取消");
OrderStatusEnum(int status, String desc) {
this.status = status;
this.desc = desc;
}
public int status;
public String desc;
}
枚舉類中先準(zhǔn)備好需要用的狀態(tài)信息艘蹋。
先用一張圖來描述整個工作機制:
然后是需要的核心代碼部分:一個管理訂單狀態(tài)的中轉(zhuǎn)站manager類,一組用于扭轉(zhuǎn)訂單狀態(tài)的operator類票灰,一組扭轉(zhuǎn)完訂單狀態(tài)后執(zhí)行后續(xù)邏輯操作的processor類女阀。
manager類需要根據(jù)對應(yīng)傳入的當(dāng)前訂單狀態(tài)、要對該訂單執(zhí)行操作來得到這個訂單的結(jié)果狀態(tài)(依靠對應(yīng)的opertor類)屑迂,然后執(zhí)行一系列需要的業(yè)務(wù)邏輯操作(編寫對應(yīng)的processor類)浸策。這樣的好處就是將訂單狀態(tài)流轉(zhuǎn)和對應(yīng)的業(yè)務(wù)處理解耦。并且也不會再有一堆繁雜的 if...else 操作惹盼。每當(dāng)需要新的訂單狀態(tài)流轉(zhuǎn)操作的時候庸汗,可以去編寫對應(yīng)的一套operator和processor組件來完成,和已有業(yè)務(wù)的分離度很高手报。
接下來貼代碼舉例
/**
* 訂單狀態(tài)流轉(zhuǎn)管理器--狀態(tài)機核心組件
* @author carpeng.gao@qunar.com
* @date 2018/10/29 19:21
**/
@Component
public class OrderStateManager {
Map<Integer, AbstractOrderOperator> orderOperatorMaps = new HashMap<Integer, AbstractOrderOperator>();
Map<Integer, AbstractOrderProcessor> orderProcessorMaps = new HashMap<Integer, AbstractOrderProcessor>();
public OrderStateManager() { }
/**
* 狀態(tài)流轉(zhuǎn)方法
* @param orderId 訂單id
* @param event 流轉(zhuǎn)的訂單操作事件
* @param status 當(dāng)前訂單狀態(tài)
* @return 扭轉(zhuǎn)后的訂單狀態(tài)
*/
public int handleEvent(final String orderId, OrderStatusEnum event, final int status) {
if (this.isFinalStatus(status)) {
throw new IllegalArgumentException("handle event can't process final state order.");
}
// 獲取對應(yīng)處理器,根據(jù)入?yún)顟B(tài)和時間獲取訂單流轉(zhuǎn)的結(jié)果狀態(tài)
AbstractOrderOperator abstractOrderOperator = this.getStateOperator(event);
int resState = abstractOrderOperator.handleEvent(status, event);
// 得到結(jié)果狀態(tài)蚯舱,在對應(yīng)的processor中處理訂單數(shù)據(jù)及其相關(guān)信息
AbstractOrderProcessor orderProcessor = this.getOrderProcessor(event);
if (!orderProcessor.process(orderId, resState)) {
throw new IllegalStateException(String.format("訂單狀態(tài)流轉(zhuǎn)失敗,訂單id:%s", orderId));
}
return resState;
}
/**
* 根據(jù)入?yún)顟B(tài)枚舉實例獲取對應(yīng)的狀態(tài)處理器
* @param event event
* @return
*/
private AbstractOrderOperator getStateOperator(OrderStatusEnum event) {
AbstractOrderOperator operator = null;
for (Map.Entry<Integer, AbstractOrderOperator> entry: orderOperatorMaps.entrySet()) {
if (event.status == entry.getKey()) {
operator = entry.getValue();
}
}
if (null == operator) {
throw new IllegalArgumentException(String.format("can't find proper operator. The parameter state :%s", event.toString()));
}
return operator;
}
/**
* 根據(jù)入?yún)顟B(tài)枚舉實例獲取對應(yīng)的狀態(tài)后處理器
* @param event event
* @return
*/
private AbstractOrderProcessor getOrderProcessor(OrderStatusEnum event) {
AbstractOrderProcessor processor = null;
for (Map.Entry<Integer, AbstractOrderProcessor> entry : orderProcessorMaps.entrySet()) {
if (event.status == entry.getKey()) {
processor = entry.getValue();
}
}
if (null == processor) {
throw new IllegalArgumentException(String.format("can't find proper processor. The parameter state :%s", event.toString()));
}
return processor;
}
/**
* 判斷是不是已完成訂單
* @param status 訂單狀態(tài)碼
* @return
*/
private boolean isFinalStatus(int status) {
return OrderStatusEnum.ORDER_FINISHED.status == status;
}
}
核心的代碼就是類中的 handleEvent 方法掩蛤。
對應(yīng)的獲取到的組件處理類示例:
/**
* @author carpeng.gao@qunar.com
* @date 2018/10/29 17:47
**/
@Data
public abstract class AbstractOrderOperator {
int status;
public abstract int handleEvent(int orderStatus, OrderStatusEnum orderStatusEnum);
}
===================================
/**
* 創(chuàng)建訂單操作狀態(tài)流轉(zhuǎn)
* @author carpeng.gao@qunar.com
* @date 2018/10/29 17:50
**/
@Component
@OrderOperator
public class CreateOrderOperator extends AbstractOrderOperator {
public CreateOrderOperator() {
super.setStatus(1);
}
@Override
public int handleEvent(int orderStatus, OrderStatusEnum orderStatusEnum) {
if (orderStatus != OrderStatusEnum.CREATE_EVENT.status && orderStatus != OrderStatusEnum.ORDER_CANCEL.status) {
throw new IllegalArgumentException(String.format("create operation can't handle the status: %s", orderStatus));
}
System.out.println("進入創(chuàng)建訂單狀態(tài)扭轉(zhuǎn)處理器...");
switch (orderStatusEnum) {
case CREATE_EVENT:
return OrderStatusEnum.FORMAL_EVENT.status;
case ORDER_CANCEL:
return OrderStatusEnum.ORDER_CANCEL.status;
default:
return getStatus();
}
}
}
后處理器
/**
* 訂單處理器
* @author carpeng.gao@qunar.com
* @date 2018/10/31 14:57
**/
@Data
public abstract class AbstractOrderProcessor {
int status;
public abstract boolean process(String orderId, Object... params);
}
================================
/**
* @author carpeng.gao@qunar.com
* @date 2018/10/31 14:59
**/
@Component
@OrderProcessor
public class CreateOrderProcessor extends AbstractOrderProcessor{
public CreateOrderProcessor() {
super.setStatus(1);
}
@Override
public boolean process(String orderId, Object... params) {
// TODO 創(chuàng)建/取消訂單對應(yīng)的數(shù)據(jù)庫修改枉昏,mq發(fā)送等操作,可以在此處process方法中完成
System.out.println("進入創(chuàng)建訂單后處理器...");
return true;
}
}
這些組件類都是依賴于spring的組件掃描注入揍鸟。如果要定制化地處理自己的組件類兄裂。可以用一些其他的技巧來處理阳藻。比如此處使用到了自定義注解徊件,通過自定義注解+自定義狀態(tài)機初始化類來完成對應(yīng)組件類的篩選與初始化蜻直。將這個初始化類加載完畢,狀態(tài)機就可以正常使用了。
/**
* 狀態(tài)機前置激活類,在spring中掃描配置此類 <br/>
* 使用自定義注解標(biāo)記對應(yīng)的狀態(tài)處理器和后置處理器并在初始化操作中完成對應(yīng)處理器的初始化萝勤。
* @author carpeng.gao@qunar.com
* @date 2018/10/31 15:30
**/
@Component
public class Initialization implements BeanPostProcessor {
@Resource
OrderStateManager manager;
@Nullable
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof AbstractOrderOperator && bean.getClass().isAnnotationPresent(OrderOperator.class) ) {
AbstractOrderOperator orderState = (AbstractOrderOperator) bean;
manager.orderOperatorMaps.put(orderState.getStatus(), orderState);
}
if (bean instanceof AbstractOrderProcessor && bean.getClass().isAnnotationPresent(OrderProcessor.class) ) {
AbstractOrderProcessor orderProcessor = (AbstractOrderProcessor) bean;
manager.orderProcessorMaps.put(orderProcessor.getStatus(), orderProcessor);
}
return bean;
}
}
這里有一個問題就是在正式開發(fā)環(huán)境中鱼冀,依賴于項目的spring環(huán)境蒋腮,需要在狀態(tài)機正式運行前將對應(yīng)的狀態(tài)扭轉(zhuǎn)組件類(operator和processor)注入到環(huán)境中割择。
四 示例
寫完公司的項目后,自己再動手編寫了一個簡化版的demo冒萄。包含了上述代碼和測試用例臊岸。
- 作者:柯基去哪了
鏈接:http://www.reibang.com/p/fe292b15a06a
來源:簡書
簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處尊流。