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