今天和你們聊『狀態模式』這個設計模式,也是因爲業務上遇到了一個極其難以維護的訂單狀態,不得不去重構。java
阿里規約其中就有一條:git
簡單來講,狀態模式用於消除冗餘的大量『if else』判斷。程序員
業務中有一個訂單表,其中訂單狀態大約有以下十多種,咱們維護在一個枚舉類型中。github
接着,咱們有一個 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();
}
複製代碼
第二步,爲每一種狀態建立對應的狀態類,並集成抽象狀態基類
第三步,分別實現各個狀態下關心的流轉操做,咱們舉例其中兩個狀態子類的實現。
這個是已支付狀態
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… 歡迎來踩!