## 引入
在閻宏博士的《JAVA與模式》一書中開頭是這樣描述狀態(State)模式的:
> 狀態模式,又稱狀態對象模式(Pattern of Objects for States),狀態模式是對象的行為模式。
狀態模式允許一個對象在其內部狀態改變的時候改變其行為。這個對象看上去就像是改變了它的類一樣。
## 定義
在很多情況下,一個對象的行為取決于它的一個或多個變化的屬性,這些屬性我們稱之為狀態,這個對象稱之為狀態對象。對于狀態對象而已,它的行為依賴于它的狀態,比如你要預訂房間,那么只有當該房間為空閑時你才能預訂,你想入住該房間也只有當你預訂了該房間或者該房間為空閑時。對于這樣的一個對象,當它在于外部事件產生互動的時候,其內部狀態就會發生改變,從而使得他的行為也隨之發生改變。
那么何為狀態模式呢?所謂狀態模式就是允許對象在內部狀態發生改變時改變它的行為,對象看起來好像修改了它的類。
## 結構
用一句話來表述,狀態模式把所研究的對象的行為包裝在不同的狀態對象里,每一個狀態對象都屬于一個抽象狀態類的一個子類。狀態模式的意圖是讓一個對象在其內部狀態改變的時候,其行為也隨之改變。狀態模式的示意性類圖如下所示:

狀態模式所涉及到的角色有:
* 環境(Context)角色,也成上下文:定義客戶端所感興趣的接口,并且保留一個具體狀態類的實例。這個具體狀態類的實例給出此環境對象的現有狀態。
* 抽象狀態(State)角色:定義一個接口,用以封裝環境(Context)對象的一個特定的狀態所對應的行為。
* 具體狀態(ConcreteState)角色:每一個具體狀態類都實現了環境(Context)的一個狀態所對應的行為。
## 認識狀態模式
**狀態和行為**
所謂對象的狀態,通常指的就是對象實例的屬性的值;而行為指的就是對象的功能,再具體點說,行為大多可以對應到方法上。
狀態模式的功能就是分離狀態的行為,通過維護狀態的變化,來調用不同狀態對應的不同功能。也就是說,狀態和行為是相關聯的,它們的關系可以描述為:狀態決定行為。
由于狀態是在運行期被改變的,因此行為也會在運行期根據狀態的改變而改變。
**行為的平行性**
注意平行線而不是平等性。所謂平行性指的是各個狀態的行為所處的層次是一樣的,相互獨立的、沒有關聯的,是根據不同的狀態來決定到底走平行線的哪一條。行為是不同的,當然對應的實現也是不同的,相互之間是不可替換的。

而平等性強調的是可替換性,大家是同一行為的不同描述或實現,因此在同一個行為發生的時候,可以根據條件挑選任意一個實現來進行相應的處理。

大家可能會發現狀態模式的結構和策略模式的結構完全一樣,但是,它們的目的、實現、本質卻是完全不一樣的。還有行為之間的特性也是狀態模式和策略模式一個很重要的區別,狀態模式的行為是平行性的,不可相互替換的;而策略模式的行為是平等性的,是可以相互替換的。
**環境和狀態處理對象**
在狀態模式中,環境(Context)是持有狀態的對象,但是環境(Context)自身并不處理跟狀態相關的行為,而是把處理狀態的功能委托給了狀態對應的狀態處理類來處理。
在具體的狀態處理類中經常需要獲取環境(Context)自身的數據,甚至在必要的時候會回調環境(Context)的方法,因此,通常將環境(Context)自身當作一個參數傳遞給具體的狀態處理類。
客戶端一般只和環境(Context)交互。客戶端可以用狀態對象來配置一個環境(Context),一旦配置完畢,就不再需要和狀態對象打交道了。客戶端通常不負責運行期間狀態的維護,也不負責決定后續到底使用哪一個具體的狀態處理對象。
## 代碼實現
首先是狀態接口:State
```
public interface State {
/**
* @desc 預訂房間
* @return void
*/
public void bookRoom();
/**
* @desc 退訂房間
* @return void
*/
public void unsubscribeRoom();
/**
* @desc 入住
* @return void
*/
public void checkInRoom();
/**
* @desc 退房
* @return void
*/
public void checkOutRoom();
}
```
然后是房間類
```
public class Room {
/*
* 房間的三個狀態
*/
State freeTimeState; //空閑狀態
State checkInState; //入住狀態
State bookedState; //預訂狀態
State state ;
public Room(){
freeTimeState = new FreeTimeState(this);
checkInState = new CheckInState(this);
bookedState = new BookedState(this);
state = freeTimeState ; //初始狀態為空閑
}
/**
* @desc 預訂房間
* @return void
*/
public void bookRoom(){
state.bookRoom();
}
/**
* @desc 退訂房間
* @return void
*/
public void unsubscribeRoom(){
state.unsubscribeRoom();
}
/**
* @desc 入住
* @return void
*/
public void checkInRoom(){
state.checkInRoom();
}
/**
* @desc 退房
* @return void
*/
public void checkOutRoom(){
state.checkOutRoom();
}
public String toString(){
return "該房間的狀態是:"+getState().getClass().getName();
}
/*
* getter和setter方法
*/
public State getFreeTimeState() {
return freeTimeState;
}
public void setFreeTimeState(State freeTimeState) {
this.freeTimeState = freeTimeState;
}
public State getCheckInState() {
return checkInState;
}
public void setCheckInState(State checkInState) {
this.checkInState = checkInState;
}
public State getBookedState() {
return bookedState;
}
public void setBookedState(State bookedState) {
this.bookedState = bookedState;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
```
然后是3個狀態類,這個三個狀態分別對于這:空閑、預訂、入住。其中空閑可以完成預訂和入住兩個動作,預訂可以完成入住和退訂兩個動作,入住可以退房。
```
/**
* @Description: 空閑狀態只能預訂和入住
*/
public class FreeTimeState implements State {
Room hotelManagement;
public FreeTimeState(Room hotelManagement){
this.hotelManagement = hotelManagement;
}
public void bookRoom() {
System.out.println("您已經成功預訂了...");
hotelManagement.setState(hotelManagement.getBookedState()); //狀態變成已經預訂
}
public void checkInRoom() {
System.out.println("您已經成功入住了...");
hotelManagement.setState(hotelManagement.getCheckInState()); //狀態變成已經入住
}
public void checkOutRoom() {
//不需要做操作
}
public void unsubscribeRoom() {
//不需要做操作
}
}
```
```
/**
* @Description: 入住狀態房間只能退房
*/
public class BookedState implements State {
Room hotelManagement;
public BookedState(Room hotelManagement) {
this.hotelManagement = hotelManagement;
}
public void bookRoom() {
System.out.println("該房間已近給預定了...");
}
public void checkInRoom() {
System.out.println("入住成功...");
hotelManagement.setState(hotelManagement.getCheckInState()); //狀態變成入住
}
public void checkOutRoom() {
//不需要做操作
}
public void unsubscribeRoom() {
System.out.println("退訂成功,歡迎下次光臨...");
hotelManagement.setState(hotelManagement.getFreeTimeState()); //變成空閑狀態
}
}
```
```
/**
* @Description: 入住可以退房
*/
public class CheckInState implements State {
Room hotelManagement;
public CheckInState(Room hotelManagement) {
this.hotelManagement = hotelManagement;
}
public void bookRoom() {
System.out.println("該房間已經入住了...");
}
public void checkInRoom() {
System.out.println("該房間已經入住了...");
}
public void checkOutRoom() {
System.out.println("退房成功....");
hotelManagement.setState(hotelManagement.getFreeTimeState()); //狀態變成空閑
}
public void unsubscribeRoom() {
//不需要做操作
}
}
```
最后是測試類
```
public class Test {
public static void main(String[] args) {
//有3間房
Room[] rooms = new Room[2];
//初始化
for(int i = 0 ; i < rooms.length ; i++){
rooms[i] = new Room();
}
//第一間房
rooms[0].bookRoom(); //預訂
rooms[0].checkInRoom(); //入住
rooms[0].bookRoom(); //預訂
System.out.println(rooms[0]);
System.out.println("---------------------------");
//第二間房
rooms[1].checkInRoom();
rooms[1].bookRoom();
rooms[1].checkOutRoom();
rooms[1].bookRoom();
System.out.println(rooms[1]);
}
}
```
## 優點
* 1、封裝了轉換規則。
* 2、枚舉可能的狀態,在枚舉狀態之前需要確定狀態種類。
* 3、將所有與某個狀態有關的行為放到一個類中,并且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行為。
* 4、允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊。
* 5、可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數。
## 缺點
* 1、狀態模式的使用必然會增加系統類和對象的個數。
* 2、狀態模式的結構與實現都較為復雜,如果使用不當將導致程序結構和代碼的混亂。
* 3、狀態模式對“開閉原則”的支持并不太好,對于可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態;而且修改某個狀態類的行為也需修改對應類的源代碼。
## 適用場景
* 1、對象的行為依賴于它的狀態(屬性)并且可以根據它的狀態改變而改變它的相關行為。
* 2、代碼中包含大量與對象狀態有關的條件語句
## 總結
* 1、狀態模式允許一個對象基于內部狀態而擁有不同的行為。
* 2、Context會將行為委托給當前狀態對象。
* 3、狀態模式對“開閉原則”支持不是很好
- java
- 設計模式
- 設計模式總覽
- 設計原則
- 工廠方法模式
- 抽象工廠模式
- 單例模式
- 建造者模式
- 原型模式
- 適配器模式
- 裝飾者模式
- 代理模式
- 外觀模式
- 橋接模式
- 組合模式
- 享元模式
- 策略模式
- 模板方法模式
- 觀察者模式
- 迭代子模式
- 責任鏈模式
- 命令模式
- 備忘錄模式
- 狀態模式
- 訪問者模式
- 中介者模式
- 解釋器模式
- 附錄
- JVM相關
- JVM內存結構
- Java虛擬機的內存組成以及堆內存介紹
- Java堆和棧
- 附錄-數據結構的堆棧和內存分配的堆區棧區的區別
- Java內存之Java 堆
- Java內存之虛擬機和內存區域概述
- Java 內存之方法區和運行時常量池
- Java 內存之直接內存(堆外內存)
- JAVA內存模型
- Java內存模型介紹
- 內存模型如何解決緩存一致性問題
- 深入理解Java內存模型——基礎
- 深入理解Java內存模型——重排序
- 深入理解Java內存模型——順序一致性
- 深入理解Java內存模型——volatile
- 深入理解Java內存模型——鎖
- 深入理解Java內存模型——final
- 深入理解Java內存模型——總結
- 內存可見性
- JAVA對象模型
- JVM內存結構 VS Java內存模型 VS Java對象模型
- Java的對象模型
- Java的對象頭
- HotSpot虛擬機
- HotSpot虛擬機對象探秘
- 深入分析Java的編譯原理
- Java虛擬機的鎖優化技術
- 對象和數組并不是都在堆上分配內存的
- 垃圾回收
- JVM內存管理及垃圾回收
- JVM 垃圾回收器工作原理及使用實例介紹
- JVM內存回收理論與實現(對象存活的判定)
- JVM參數及調優
- CMS GC日志分析
- JVM實用參數(一)JVM類型以及編譯器模式
- JVM實用參數(二)參數分類和即時(JIT)編譯器診斷
- JVM實用參數(三)打印所有XX參數及值
- JVM實用參數(四)內存調優
- JVM實用參數(五)新生代垃圾回收
- JVM實用參數(六) 吞吐量收集器
- JVM實用參數(七)CMS收集器
- JVM實用參數(八)GC日志
- Java性能調優原則
- JVM 優化經驗總結
- 面試題整理
- 面試題1
- java日志規約
- Spring安全
- OAtuth2.0簡介
- Spring Session 簡介(一)
- Spring Session 簡介(二)
- Spring Session 簡介(三)
- Spring Security 簡介(一)
- Spring Security 簡介(二)
- Spring Security 簡介(三)
- Spring Security 簡介(四)
- Spring Security 簡介(五)
- Spring Security Oauth2 (一)
- Spring Security Oauth2 (二)
- Spring Security Oauth2 (三)
- SpringBoot
- Shiro
- Shiro和Spring Security對比
- Shiro簡介
- Session、Cookie和Cache
- Web Socket
- Spring WebFlux