網(wǎng)站開發(fā) 網(wǎng)頁制作網(wǎng)頁推廣鏈接怎么做
狀態(tài)模式
一. 說明
狀態(tài)模式通常描述一個(gè)類不同行為的多個(gè)狀態(tài)變更,對(duì)象的行為依賴它的狀態(tài),它是一種行為型模式。
狀態(tài)模式可以用來消除代碼中大量的if-else結(jié)構(gòu),它明確對(duì)象是有狀態(tài)的、對(duì)象的不同狀態(tài)對(duì)應(yīng)的行為不一樣、行為之間是可以切換的。簡(jiǎn)單來講,就是對(duì)象的狀態(tài)只允許在某個(gè)或某些行為下發(fā)生改變,否則不允許該行為操作對(duì)象狀態(tài)。
我們用生活中購(gòu)物訂單的例子來說明狀態(tài)模式,圖示如下:
從圖示中可以看出:
- 未支付的訂單只能被用戶做支付或者取消操作
- 已支付的訂單只能被用戶發(fā)貨前催單 (按催單對(duì)象不同分為兩種催單形式)
- 已發(fā)貨的訂單只能被用戶發(fā)貨后催單
- 已簽收的訂單只能被用戶評(píng)價(jià)或刪除
- 已刪除的訂單用戶已經(jīng)看不到了,無法做任何操作
總的來說就是用戶訂單的某個(gè)狀態(tài)只能被用戶的某個(gè)對(duì)應(yīng)行為所改變,而其他行為是無法操作的。
二.應(yīng)用場(chǎng)景
- 線上購(gòu)物時(shí)的訂單狀態(tài)隨著不同行為而變化
- 營(yíng)銷活動(dòng)的上線審批流程,活動(dòng)也是有狀態(tài)變化的
- 游戲時(shí)控制的角色在不同環(huán)境下有不同的狀態(tài)(buff加成或debuff時(shí)角色屬性有不同的變化)
三.代碼示例
以購(gòu)物下單為例,我們?cè)贠rderService里實(shí)現(xiàn)幾個(gè)行為:支付、取消訂單、催單、判斷是否可評(píng)價(jià)、刪除訂單這幾個(gè)行為,按一般的寫法是這樣的:
先創(chuàng)建OrderService接口,提供幾個(gè)方法
public interface OrderService {/*** 訂單支付*/boolean pay(OrderDTO orderDTO);/*** 訂單取消*/boolean cancel(OrderDTO orderDTO);/*** 催單*/boolean reminder(OrderDTO orderDTO);/*** 訂單評(píng)價(jià)校驗(yàn)*/boolean isEvaluable(OrderDTO orderDTO);/*** 刪除訂單*/boolean delete(OrderDTO orderDTO);
}
實(shí)現(xiàn)Service接口,每個(gè)行為都需要判斷在特定狀態(tài)下才能操作
public class OrderServiceImpl implements OrderService {@Overridepublic boolean pay(OrderDTO orderDTO) {if (orderDTO.getState() == StateEnum.UNPAID) {System.out.println("pay-訂單支付流程");orderDTO.setState(StateEnum.PAID);System.out.println("pay-訂單支付成功");return true;}System.out.println("pay-該訂單無法支付,當(dāng)前狀態(tài)為:" + orderDTO.getState().getName());return false;}@Overridepublic boolean cancel(OrderDTO orderDTO) {if (orderDTO.getState() == StateEnum.UNPAID) {System.out.println("cancel-訂單取消流程");orderDTO.setState(StateEnum.CANCEL);System.out.println("cancel-訂單取消成功");return true;}System.out.println("cancel-該訂單無法取消,當(dāng)前狀態(tài)為:" + orderDTO.getState().getName());return false;}@Overridepublic boolean reminder(OrderDTO orderDTO) {if (orderDTO.getState() == StateEnum.PAID) {System.out.println("reminder-訂單催單成功,催單對(duì)象為賣家");return true;}if (orderDTO.getState() == StateEnum.DELIVERED) {System.out.println("reminder-訂單催單成功,催單對(duì)象為物流");return true;}System.out.println("reminder-該訂單無法催單,當(dāng)前狀態(tài)為:" + orderDTO.getState().getName());return false;}@Overridepublic boolean isEvaluable(OrderDTO orderDTO) {if (orderDTO.getState() == StateEnum.SIGNED) {System.out.println("evaluable-訂單可以評(píng)價(jià)");return true;}System.out.println("evaluable-該訂單無法評(píng)價(jià),當(dāng)前狀態(tài)為:" + orderDTO.getState().getName());return false;}@Overridepublic boolean delete(OrderDTO orderDTO) {if (orderDTO.getState() == StateEnum.SIGNED) {System.out.println("delete-訂單刪除流程");orderDTO.setState(StateEnum.DELETED);System.out.println("delete-訂單刪除成功");return true;}System.out.println("delete-訂單無法刪除,當(dāng)前狀態(tài)為:" + orderDTO.getState().getName());return false;}
}
編寫測(cè)試代碼
public static void main(String[] args) {OrderDTO orderDTO = new OrderDTO();orderDTO.setOrderId("202400001");orderDTO.setState(StateEnum.UNPAID);OrderService service = new OrderServiceImpl();service.pay(orderDTO);service.pay(orderDTO);service.cancel(orderDTO);service.reminder(orderDTO);orderDTO.setState(StateEnum.DELIVERED);service.reminder(orderDTO);orderDTO.setState(StateEnum.SIGNED);service.isEvaluable(orderDTO);service.delete(orderDTO);}
如上圖代碼所示,這些代碼的特點(diǎn)是,OrderService下的每一方法(即行為),都需要考慮訂單的各個(gè)狀態(tài),業(yè)務(wù)邏輯相對(duì)來說復(fù)雜一些,如果要增加一個(gè)新的訂單狀態(tài),每個(gè)方法都要按需調(diào)整,擴(kuò)展性不高,不符合開閉原則。
我們?cè)赟pringBoot環(huán)境中使用狀態(tài)模式重構(gòu)以上的訂單流程:
首先需要引入另一組service,用來表示訂單狀態(tài)的一組業(yè)務(wù),命名為OrderStateService,提供與OrderService一致的行為方法
public interface IOrderStateService {StateEnum getState();/*** 訂單支付*/boolean pay(OrderDTO orderDTO);/*** 訂單取消*/boolean cancel(OrderDTO orderDTO);/*** 催單*/boolean reminder(OrderDTO orderDTO);/*** 訂單評(píng)價(jià)校驗(yàn)*/boolean isEvaluable(OrderDTO orderDTO);/*** 刪除訂單*/boolean delete(OrderDTO orderDTO);
}
再編寫抽象業(yè)務(wù),實(shí)現(xiàn)接口的方法,提供默認(rèn)的實(shí)現(xiàn),默認(rèn)實(shí)現(xiàn)都是該訂單狀態(tài)不支持該行為操作,我們把真正的行為實(shí)現(xiàn)落地到具體的實(shí)現(xiàn)類上。
@Slf4j
public abstract class AbstractOrderStateService implements IOrderStateService {@Overridepublic boolean pay(OrderDTO orderDTO) {log.info("pay-該訂單無法支付,當(dāng)前狀態(tài)為:{}", orderDTO.getState().getName());return false;}@Overridepublic boolean cancel(OrderDTO orderDTO) {log.info("cancel-該訂單無法取消,當(dāng)前狀態(tài)為:{}", orderDTO.getState().getName());return false;}@Overridepublic boolean reminder(OrderDTO orderDTO) {log.info("reminder-該訂單無法催單,當(dāng)前狀態(tài)為:{}", orderDTO.getState().getName());return false;}@Overridepublic boolean isEvaluable(OrderDTO orderDTO) {log.info("evaluable-該訂單無法評(píng)價(jià),當(dāng)前狀態(tài)為:{}", orderDTO.getState().getName());return false;}@Overridepublic boolean delete(OrderDTO orderDTO) {log.info("delete-該訂單無法刪除,當(dāng)前狀態(tài)為:{}", orderDTO.getState().getName());return false;}
}
再重寫每個(gè)狀態(tài)對(duì)應(yīng)允許的行為操作,一旦重寫即表示該狀態(tài)允許該行為操作
//未支付狀態(tài)重寫支付、取消、刪除方法
@Slf4j
@Service
public class UnpaidOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.UNPAID;}@Overridepublic boolean pay(OrderDTO orderDTO) {log.info("pay-訂單支付成功,當(dāng)前訂單狀態(tài):{}", orderDTO.getState().getName());orderDTO.setState(StateEnum.PAID);return true;}@Overridepublic boolean cancel(OrderDTO orderDTO) {log.info("cancel-訂單取消成功,當(dāng)前訂單狀態(tài):{}", orderDTO.getState().getName());orderDTO.setState(StateEnum.CANCEL);return true;}@Overridepublic boolean delete(OrderDTO orderDTO) {log.info("delete-訂單取消成功,當(dāng)前訂單狀態(tài):{}", orderDTO.getState().getName());orderDTO.setState(StateEnum.DELETED);return true;}
}
//已支付狀態(tài)重寫催單方法
@Slf4j
@Service
public class PaidOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.PAID;}@Overridepublic boolean reminder(OrderDTO orderDTO) {log.info("發(fā)貨前催單成功, 催單對(duì)象為賣家,當(dāng)前訂單狀態(tài):{}", orderDTO.getState().getName());return true;}
}
//已發(fā)貨狀態(tài)重寫催單方法
@Slf4j
@Service
public class DeliveredOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.DELIVERED;}@Overridepublic boolean reminder(OrderDTO orderDTO) {log.info("發(fā)貨后催單成功, 催單對(duì)象為物流,當(dāng)前訂單狀態(tài):{}", orderDTO.getState().getName());return true;}
}
//已簽收狀態(tài)重寫判斷評(píng)價(jià)和刪除方法
@Slf4j
@Service
public class SignedOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.SIGNED;}@Overridepublic boolean isEvaluable(OrderDTO orderDTO) {log.info("訂單允許評(píng)價(jià),當(dāng)前訂單狀態(tài):{}", orderDTO.getState().getName());return true;}@Overridepublic boolean delete(OrderDTO orderDTO) {log.info("訂單刪除成功,當(dāng)前訂單狀態(tài):{}", orderDTO.getState().getName());orderDTO.setState(StateEnum.DELETED);return true;}
}
//已刪除狀態(tài)不做任何操作
@Service
public class DeletedOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.DELETED;}
}
//已取消狀態(tài)不做任何操作
@Service
public class CancelOrderStateService extends AbstractOrderStateService {@Overridepublic StateEnum getState() {return StateEnum.CANCEL;}
}
編寫獲取具體StateService的工廠類
@Component
public class OrderStateFactory {private final Map<StateEnum, IOrderStateService> stateMap = new HashMap<>();@Resourceprivate Set<IOrderStateService> orderStateServiceSet;@PostConstructpublic void init() {orderStateServiceSet.forEach(service -> {stateMap.put(service.getState(), service);});}public IOrderStateService getOrderStateService(StateEnum state) {return stateMap.get(state);}}
改造OrderService的實(shí)現(xiàn)類
@Service
public class OrderServiceImpl implements OrderService {@Resourceprivate OrderStateFactory orderStateFactory;@Overridepublic boolean pay(OrderDTO orderDTO) {return orderStateFactory.getOrderStateService(orderDTO.getState()).pay(orderDTO);}@Overridepublic boolean cancel(OrderDTO orderDTO) {return orderStateFactory.getOrderStateService(orderDTO.getState()).cancel(orderDTO);}@Overridepublic boolean reminder(OrderDTO orderDTO) {return orderStateFactory.getOrderStateService(orderDTO.getState()).reminder(orderDTO);}@Overridepublic boolean isEvaluable(OrderDTO orderDTO) {return orderStateFactory.getOrderStateService(orderDTO.getState()).isEvaluable(orderDTO);}@Overridepublic boolean delete(OrderDTO orderDTO) {return orderStateFactory.getOrderStateService(orderDTO.getState()).delete(orderDTO);}
}
編寫測(cè)試代碼
public static void test(){OrderServiceImpl service = (OrderServiceImpl) applicationContext.getBean("orderServiceImpl");OrderDTO orderDTO = new OrderDTO();orderDTO.setOrderId("202400001");orderDTO.setState(StateEnum.UNPAID);service.pay(orderDTO);service.pay(orderDTO);service.cancel(orderDTO);service.reminder(orderDTO);orderDTO.setState(StateEnum.DELIVERED);service.reminder(orderDTO);orderDTO.setState(StateEnum.SIGNED);service.isEvaluable(orderDTO);service.delete(orderDTO);}
可以看到,重構(gòu)后實(shí)現(xiàn)了相同的業(yè)務(wù)功能,并且完全干掉了if-else的判斷代碼。后續(xù)新增某個(gè)訂單狀態(tài),我們只需要擴(kuò)展StateService的具體狀態(tài)實(shí)現(xiàn)即可。雖然整個(gè)模塊的類增加了,略顯復(fù)雜,但我們犧牲復(fù)雜性去換取高可維護(hù)性和擴(kuò)展性是相當(dāng)值得的。
四. 總結(jié)
在狀態(tài)模式中,我們把每一種狀態(tài)都獨(dú)立成一個(gè)類進(jìn)行處理,這滿足了單一職責(zé)和開閉原則。狀態(tài)模式強(qiáng)調(diào)的是行為和狀態(tài)的對(duì)應(yīng),只有在特定的業(yè)務(wù)場(chǎng)景下使用它,在通常三層架構(gòu)的web開發(fā)中,對(duì)象的狀態(tài)和對(duì)象行為一般都是分離的,所以在開發(fā)中我們使用地并不多。
狀態(tài)模式和狀態(tài)機(jī)也是有關(guān)系的,狀態(tài)機(jī)類似一種開發(fā)引擎便于我們?cè)陂_發(fā)中使用,它同樣采用的是狀態(tài)模式的思想實(shí)現(xiàn)的。