被各類嵌套判斷噁心的你,想到狀態模式了嗎?

今天和你們聊『狀態模式』這個設計模式,也是因爲業務上遇到了一個極其難以維護的訂單狀態,不得不去重構。java

阿里規約其中就有一條:git

image

簡單來講,狀態模式用於消除冗餘的大量『if else』判斷。程序員

舉個例子

業務中有一個訂單表,其中訂單狀態大約有以下十多種,咱們維護在一個枚舉類型中。github

image

接着,咱們有一個 service,用於流轉狀態用:面試

public interface OrderService {

    /**
     * 用戶發起退款,流轉當前訂單到退款狀態
     * @param order
     * @return
     */
    boolean refund(Order order);

    /**
     * 用戶取消訂單
     * @param order
     * @return
     */
    boolean cancle(Order order);
}
複製代碼

而後他的實現類是這樣:設計模式

@Service
public class OrderServiceImpl implements OrderService {

    @Override
    public boolean refund(Order order) {
        boolean flag = true;
        //判斷當前訂單狀態
        if (order.getOrderState() == OrderState.WAITSHIP.getCode() ||
                order.getOrderState() == OrderState.PAYED.getCode()){
            //若是是已付款、待發貨,直接退款成功
            order.setOrderState(OrderState.REFUND.getCode());
        }else if (order.getOrderState() == OrderState.ALREADYSHIP.getCode() ||
                    order.getOrderState() == OrderState.ENDED.getCode()){
            //若是是已發貨了或交易完成了,流轉到退款中,等待商家審覈
            order.setOrderState(OrderState.REFUNDING.getCode());
        }else {
            //其餘狀態都是異常狀態,拒絕流轉
            flag = false;
        }
        return flag;
    }

    @Override
    public boolean cancle(Order order) {
        boolean flag = true;
        //判斷當前狀態
        if (order.getOrderState() == OrderState.NOTPAY.getCode()){
            //只有未付款狀態能夠取消訂單
            order.setOrderState(OrderState.CANCELED.getCode());
        }else {
            flag = false;
        }
        return flag;
    }
}
複製代碼

一個簡單的 refund 流轉退款狀態至少須要上面這麼一大坨的 『if else』判斷,下面的 cancle 取消訂單狀態的流轉稍微簡單些。微信

這裏我也只精簡了部分代碼,實際上要複雜的更多,但好在狀態之間的依賴性尚未太強,沒有出現嵌套多層『if else』判斷,狀態模式怎麼改?markdown

狀態模式

傳統的判斷模式之因此會有不少的『if else』判斷,本質上就是不知道當前訂單實什麼狀態,因此須要判斷當前訂單在不一樣狀態下該怎麼流轉。ide

第一步:建立一個抽象狀態基類,在其中定義全部的狀態流轉操做,這裏我只寫了兩個,實際業務中確定會有不少不少狀態間的跳轉。oop

@Slf4j
public abstract class AbstOrderState {

    /**
     * 用戶發起退款,流轉當前訂單到退款狀態
     * @param order
     * @return
     */
    public boolean refund(Order order){
        log.info("沒法從狀態:{} 流轉到退款狀態",getState());
        return false;
    }

    /**
     * 用戶取消訂單
     * @param order
     * @return
     */
    public boolean cancle(Order order){
        log.info("沒法從狀態:{} 流轉到取消訂單狀態",getState());
        return false;
    }

    /**
     * 模板方法,獲取當前狀態
     * @return
     */
    public abstract String getState();
}
複製代碼

第二步,爲每一種狀態建立對應的狀態類,並集成抽象狀態基類

image

第三步,分別實現各個狀態下關心的流轉操做,咱們舉例其中兩個狀態子類的實現。

這個是已支付狀態

public class PayEdState extends AbstOrderState {

    @Override
    public boolean refund(Order order){
        //當前狀態是已付款,目標狀態是退款
        order.setOrderState(OrderState.REFUND.getCode());
        return true;
    }

    //當前狀態是已付款,目標狀態是取消訂單狀態,沒法流轉,異常的狀態
    //無需重寫,使用抽象基類默認實現,返回失敗便可
//    @Override
//    public boolean cancle(Order order){
//        return false;
//    }

    @Override
    public String getState(){
        return OrderState.PAYED.getDesc();
    }
}
複製代碼

這個是未付款狀態

public class NotPayState extends AbstOrderState {

    @Override
    public boolean refund(Order order){
        //當前狀態是未付款,目標狀態是退款,直接成功
        return true;
    }

    @Override
    public boolean cancle(Order order){
        //當前狀態是未付款,目標狀態是取消訂單,直接成功
        return true;
    }

    @Override
    public String getState() {
        return OrderState.NOTPAY.getDesc();
    }
}
複製代碼

第四步,如何使用

@Test
    public void test(){
        //初始化一個訂單,默認未支付狀態
        Order order = new Order();
        order.setOrderState(OrderState.NOTPAY.getCode());

        //我想退款
        AbstOrderState state = OrderState.getStateByCode(order.getOrderState());
        if (state.refund(order)){
            log.info("ok");
        }else {
            log.info("failed");
        }
    }
複製代碼

至此,狀態模式演示完畢,其實細心的你會發現,狀態模式中未出現一行『if else』,但缺點就是多了不少類,但這是抽象性的必然結果。

對比一下

實際訂單狀態這個例子並非很完美契合狀態模式,由於狀態之間依賴性沒那麼強,不多可能會出現嵌套判斷,但效果是很顯然的。

試想一下,若是之後個人訂單增長了一個狀態叫『凍結狀態』,那麼我只須要建立一個新的狀態類,並只關心我這個凍結狀態相關的流轉操做,重寫一下就行了,根本不用跑到以前的邏輯裏改啊改。

狀態模式仍是一個很是優秀的設計模式,推薦你們在項目裏使用起來,除了初始編碼的時候麻煩一點,後續的維護以及擴展真的近乎零成本。

近期會整理一個設計模式系列,分別講講 23 種設計模式,感興趣的能夠關注下哦~


關注公衆不迷路,一個愛分享的程序員。 公衆號回覆「1024」加做者微信一塊兒探討學習! 公衆號回覆「面試題」送你一份面試題以及做者的做答答案 每篇文章用到的全部案例代碼素材都會上傳我我的 github github.com/SingleYam/o… 歡迎來踩!
相關文章
相關標籤/搜索