一、概念 狀態模式:允許對象在內部狀態改變時改變它的行為,對象看起來好像修改了它的類。這個模式將狀態封裝成為獨立的類,並將動作委托到代表當前狀態的對象,我們知道行為會隨著內部狀態而改變。 一個對象“看起來好像修改了它的類”是什麼意思呢?從客戶的視角來看:如果說你使用的對象能夠完全改變它的行為,那麼你 ...
一、概念
- 狀態模式:允許對象在內部狀態改變時改變它的行為,對象看起來好像修改了它的類。這個模式將狀態封裝成為獨立的類,並將動作委托到代表當前狀態的對象,我們知道行為會隨著內部狀態而改變。
- 一個對象“看起來好像修改了它的類”是什麼意思呢?從客戶的視角來看:如果說你使用的對象能夠完全改變它的行為,那麼你會覺得,這個對象實際上是從別的類實例化而來的。然而,實際上,狀態模式是在使用組合通過簡單引用不同的狀態對象來造成類改變的假象。
- 角色:
1、上下文(Context): 定義客戶感興趣的介面。通常負責具體狀態的切換。維護多個 ConcreteState 子類的實例,每個實例分別代表不同的狀態。
2、抽象狀態類(State): 定義一個介面或抽象類以封裝與 Context 的狀態相關的行為。
3、具體狀態類(ConcreteState): 實現 State,實現與 Context 的一個狀態相關的行為。每個 ConcreteState 都表示 Context 的一個狀態。
二、Demo 實現
我們以一個網約車訂單場景來做一個簡單的 Demo 示例。訂單分為四個狀態 —— New(新建)、Running(進行中)、Cancel(取消)、End(結束)。當乘客下單時,訂單狀態進入 New 狀態。這時,如果有司機接單,訂單狀態進入 Running 狀態;如果無司機接單,乘客取消訂單,訂單進入 Cancel 狀態。最後,乘客評價,Running 狀態的訂單進入 End 狀態。
1、抽象狀態類
* @Description: 抽象狀態
* @author: cuixiuyin
* @date: 2019/01/16 08:57
*/
public interface State {
/***
* @Description 乘客下單,訂單創建
* @author cuixiuyin
* @date 2019/01/16 08:59
*/
void orderCreate();
/***
* @Description 乘客取消,訂單取消
* @author cuixiuyin
* @date 2019/01/16 08:59
*/
void orderCancel();
/***
* @Description 司機接單,訂單匹配
* @author cuixiuyin
* @date 2019/01/16 08:59
*/
void orderMatch();
/***
* @Description 乘客評價,訂單結束
* @author cuixiuyin
* @date 2019/01/16 08:59
*/
void evaluation();
}
這裡,我們定義了一個抽象狀態類,封裝了與 Context 的狀態有關的行為 —— 乘客下單、乘客取消、司機接單、乘客評價。
2、具體狀態類
- New 狀態:
public class NewState implements State {
private Order order;
public NewState(Order order) {
this.order = order;
}
@Override
public void orderCreate() {
System.out.println("您有一個訂單等待司機接單,不可新建訂單");
}
@Override
public void orderCancel() {
System.out.println("乘客取消訂單,訂單取消中...");
// 改變狀態 New -> Cancel
order.setState(order.getCancelState());
}
@Override
public void orderMatch() {
System.out.println("司機接單中...");
// 改變狀態 New -> Running
order.setState(order.getRunningState());
}
@Override
public void evaluation() {
System.out.println("新創建訂單,不可評價");
}
}
- Cancel 狀態:
public class CancelState implements State {
private Order order;
public CancelState(Order order) {
this.order = order;
}
@Override
public void orderCreate() {
System.out.println("乘客下單,訂單新建中...");
// 改變狀態(取消後可以開始新一輪叫單) Cancel -> New
order.setState(order.getNewState());
}
@Override
public void orderCancel() {
System.out.println("訂單已被取消,無可取消訂單...");
}
@Override
public void orderMatch() {
System.out.println("無法接單,該訂單已被乘客取消...");
}
@Override
public void evaluation() {
System.out.println("已取消訂單不可評價...");
}
}
- Running 狀態:
public class RunningState implements State {
private Order order;
public RunningState(Order order) {
this.order = order;
}
@Override
public void orderCreate() {
System.out.println("您有訂單在進行中,不可新建訂單...");
}
@Override
public void orderCancel() {
System.out.println("正在行程中的訂單,無法取消");
}
@Override
public void orderMatch() {
System.out.println("您有訂單在進行中,不可接其他單");
}
@Override
public void evaluation() {
System.out.println("乘客評價訂單,訂單結束...");
// 改變狀態 Running -> End
order.setState(order.getEndState());
}
}
- End 狀態:
public class EndState implements State {
private Order order;
public EndState(Order order) {
this.order = order;
}
@Override
public void orderCreate() {
System.out.println("乘客下單,訂單新建中...");
// 改變狀態(訂單結束後可以開始新一輪叫單) End -> New
order.setState(order.getNewState());
}
@Override
public void orderCancel() {
System.out.println("無可取消訂單");
}
@Override
public void orderMatch() {
System.out.println("無可匹配訂單");
}
@Override
public void evaluation() {
System.out.println("無可評價訂單");
}
}
這裡,我們定義了四個具體狀態類(New、Cancel、Running、End),每個狀態類都持有 Context 的引用,狀態之間的切換由 Context 實例來進行。
3、Context 上下文
public class Order {
private State newState;
private State cancelState;
private State runningState;
private State endState;
// 預設訂單已結束,可開始新一輪叫單
private State state;
public Order() {
newState = new NewState(this);
cancelState = new CancelState(this);
runningState = new RunningState(this);
endState = new EndState(this);
state = endState;
}
void orderCreate() {
state.orderCreate();
}
void orderCancel() {
state.orderCancel();
}
void orderMatch() {
state.orderMatch();
}
void evaluation() {
state.evaluation();
}
public void setState(State state) {
this.state = state;
}
public State getNewState() {
return newState;
}
public State getCancelState() {
return cancelState;
}
public State getRunningState() {
return runningState;
}
public State getEndState() {
return endState;
}
}
這裡,我們定義了Context,Context 持有所有需要轉換的狀態實例,並把每一個行為動作都委托給狀態來進行。
4、測試
public class Test {
public static void main(String[] args) {
Order order = new Order();
//1、新建訂單
order.orderCreate();
//2、取消訂單
order.orderCancel();
//3、新建訂單
order.orderCreate();
//4、司機接單
order.orderMatch();
//5、嘗試取消訂單
order.orderCancel();
//6、乘客評價
order.evaluation();
}
}
演示源代碼:https://github.com/JMCuixy/design-patterns
三、總結
- 狀態模式允許一個對象基於內部狀態而擁有不同的行為。
- 通過將每個狀態封裝進一個類,我們把以後需要做的任何改變局部化了,便於擴展和理解。
- 客戶不會直接改變 Context 的狀態。全盤瞭解狀態是 Context 的工作。Context 會將行為委托給當前狀態對象。
- 狀態類可以被多個 Context 實例共用。
- 基本常識:策略模式和狀態模式時雙胞胎(它們有相同的類圖),在出生時才分開(它們的意圖不同)。策略模式和狀態模式有什麼區別呢?
1、以狀態模式而言,我們將一群行為封裝在狀態對象中,Context 的行為可委托到那些狀態對象中的一個。隨著時間的流逝,當前狀態在狀態對象集合中游走改變,以反映出 Context 內部的狀態,因此, Context 的行為也會跟著改變。
2、以策略模式而言,客戶通常主動指定 Context 所要組合的策略對象是哪一個。現在,固然策略模式讓我們具有彈性,能夠運行時改變策略,但對於某個 Context 對象來說,通常都只有一個最適合的策略對象。
3、一般來說,我們把狀態模式想成是不用在 Context 中放置許多條件判斷的替代方案。通過將行為包裝進狀態對象中,你可以通過在 Context 內簡單的改變狀態對象來改變 Context 的行為。
4、一般來說,我們將策略模式想成是除了繼承之外的一種彈性替代方案。有了策略模式,我們可以通過組合不同的對象來改變行為。
- 使用狀態模式通常會導致設計中類的數目大量增加。(這幾乎是所有設計模式的通病)