這是我參與8月更文挑戰的第8天,活動詳情查看:8月更文挑戰java
歡迎來到今天的學習,今天咱們一塊兒來學習下你可能不多據說,可是隨處可用的----狀態模式。多嘮叨幾句,我本月將會對java的設計模式精講,歡迎點擊頭像,關注個人專欄,我會持續更新,加油!spring
系列文章:設計模式
設計模式之單例模式markdown
設計模式之代理模式ide
設計模式之訪問者模式post
設計模式之適配器模式單元測試
...持續更新中
話很少說,進入正題
顧名思義,應用場景確定是要隨時改變某個事件業務的狀態,好比咱們的支付的訂單訂單,咱們購買商品到用戶收貨完成的一系列,均可以用到狀態模式。
它的原始定義是:容許一個對象在其內部狀態改變時改變它的行爲,對象看起來彷佛修改了本身的類同樣。
簡單來講,狀態模式就是讓一個對象經過定義一系列狀態的變化來控制行爲的變化,好比咱們提到的線上商品購買。給購買的物品定義幾個包裹運送狀態,已下單、運送中、已簽收等狀態的調整,也就是說,當包裹的狀態發生改變時,就會觸發相應的外部操做。
咱們看下圖。大概就是這樣子(此圖來源於網絡):
從圖中,咱們能看出狀態模式包含的關鍵角色有三個:
上下文信息類(Context):實際上就是存儲當前狀態的類,對外提供更新狀態的操做。(通常你看到Context字眼 上下文信息類,spring當中有不少相似的)
抽象狀態類(State):能夠是一個接口或抽象類,用於定義聲明狀態更新的操做方法有哪些
具體狀態類(StateA 等):實現抽象狀態類定義的方法,根據具體的場景來指定對應狀態改變後的代碼實現邏輯
接下來咱們拿支付訂單狀態實際場景結合代碼來深刻理解下狀態模式
爲了幫助你更好地理解狀態模式的適用場景,下面咱們仍是經過一個簡單的例子來演示一下。在用戶支付一筆訂單的過程當中,當咱們選定好了商品提交訂單後,用戶點擊支付,拉起支付,到最後的支付成功。這裏咱們定義 4 種簡單的訂單狀態:建立訂單、支付中、取消支付、支付成功。以下圖所示:
咱們針對上面的簡圖來用狀態模式代碼實現下業務
首先,咱們來定義訂單的狀態 OrderState,在接口中聲明一個更新狀態的方法 updateState(),該方法接收訂單上下文信息類 OrderContext 做爲參數。
//定義訂單狀態接口
public interface OrderState {
/** * 定義了4種狀態 * 1 - 建立訂單 * 2 - 訂單支付中 * 3 - 用戶中途取消支付 * 4 - 支付成功 * @param ctx */
void updateState(OrderContext ctx);
}
複製代碼
而後咱們再來詳細定義上下文信息類 orderContext,其中包含一個當前狀態 OrderState 和一個訂單編號(orderNo)
public class OrderContext {
private OrderState currentState; //訂單狀態
private String orderNo; //訂單編號
public OrderContext(OrderState currentState, String orderNo) {
this.currentState = currentState;
this.orderNo = orderNo;
//默認初始化建立訂單
if(currentState == null) {
this.currentState = Acknowledged.instance();
}
}
public OrderState getCurrentState() {
return currentState;
}
public void setCurrentState(OrderState currentState) {
this.currentState = currentState;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
//開始修改狀態(當前對象攜帶的信息)
public void update() {
currentState.updateState(this);
}
}
複製代碼
接下來,咱們依次定義具體的狀態類:建立訂單(CreateOrder)、訂單支付(OrderProcessing)、取消支付(CancelOrder)、支付成功(OrderPaySuccess)。每個類都會實現 updateState() 方法,同時使用單例模式模擬狀態的惟一性。
//建立訂單
public class CreateOrder implements OrderState {
//Singleton
private static CreateOrder instance = new CreateOrder();
private CreateOrder() {}
public static CreateOrder instance() {
return instance;
}
@Override
public void updateState(OrderContext ctx) {
System.out.println("->. -> 開始建立訂單");
//建立完訂單 將訂單修改成訂單支付中......
ctx.setCurrentState(OrderProcessing.instance());
}
}
//訂單支付中
public class OrderProcessing implements OrderState {
//Singleton
private static OrderProcessing instance = new OrderProcessing();
private OrderProcessing() {}
public static OrderProcessing instance() {
return instance;
}
@Override
public void updateState(OrderContext ctx) {
System.out.println("-> -> 訂單支付中");
//接着改成支付成功(不考慮用戶中途取消)
ctx.setCurrentState(InTransition.instance());
}
}
//訂單支付成功
public class OrderPaySuccess implements OrderState {
//Singleton
private static OrderPaySuccess instance = new OrderPaySuccess();
private OrderPaySuccess() {}
public static OrderPaySuccess instance() {
return instance;
}
@Override
public void updateState(OrderContext ctx) {
System.out.println("-> -> 用戶支付成功 end");
}
}
複製代碼
最後,咱們運行一個單元測試,經過執行上下文信息類的更新操做了變動狀態。
public class Client {
public static void main(String[] args) {
OrderContext ctx = new OrderContext(null, "123456789");
ctx.update();
ctx.update();
}
}
//輸出
->. -> 開始建立訂單
-> -> 訂單支付中
-> -> 用戶支付成功 end
複製代碼
OK,到這裏咱們的代碼部分就結束了。可能會有人問了,爲何使用狀態模式呢,緣由主要有如下兩個:
第一個,下降耦合性。當要設計的業務具備複雜的狀態變遷時,咱們指望經過狀態變化來快速進行變動操做,並下降代碼耦合性。
第二個避免增長代碼的複雜性,要否則就得寫滿屏的if eles
寫到這裏突然想到 還有個例子更加讓你容易理解,就是線程池的五種狀態:一個線程的生命週期裏有五種狀態,只有在得到當前狀態後才能肯定下一個狀態。
有得必有舍,任何模式都是這樣的,咱們考慮下狀態模式的缺點。這樣之後有利於咱們更好的優化代碼:
形成不少零散類。 狀態模式由於須要對每個狀態定義一個具體狀態類,因此勢必會增長系統類和對象的個數
狀態切換關係越複雜,代碼實現難度越高,若是業務及其複雜,又比較耗時,不建議此中模式,應採用異步形式。
不知足開閉原,若是要改邏輯,好比要動具體裏面的業務代碼。
OK,狀態模式 今天咱們就講到這裏了!
感謝你的閱讀,若是你感受學到了東西,麻煩您點贊,關注。
我已經將本章收錄在專題裏,點擊下方專題,關注專欄,我會天天發表乾貨,本月我會持續輸入設計模式。
加油! 咱們下期再見!