概念:狀態模式把所研究的對象的行為包裝在不同的狀態對象裡,每一個狀態對象都屬於一個抽象狀態類的一個子類。狀態模式的意圖是讓一個對象在其內部狀態改變的時候,其行為也隨之改變,也就是不同狀態對應不同的行為。狀態模式的示意性類圖如下所示:

UML類圖:Context:可以理解成控制類
State是狀態接口
ConcreteStateA和ConcreteStateB可以認為是接口的實現,也就是具體的狀態實現類。
使用場景:對象的狀態決定對象的行為,在運行時根據狀態動態調整對象的行為。
代碼中有復雜的if else判斷,且這些分支判斷和狀態有關系。
實際場景:(1)電視開關機狀態,電視開機狀態下才可以進行各種操作,關機狀態下,不能進行各項操作。
(2)WIFI狀態,WIFI開狀態下可以連接WIFI,關閉狀態不可進行操作。
(3)登錄狀態,這個在開發中較為常用,一般在進入系統實現分享轉發等操作時要先判斷用戶的登錄狀態,若已經登錄則可進行操作,否則提示用戶登錄。
我們來實現場景1.
首先創建一個狀態接口(對應UML類圖中的State接口):
package com.state.demo;
public interface TvState {
/**
* 電視狀態的接口,裡面提供四種方法
*/
void nextChanel();
void preChanel();
void turnUp();
void turnDown();
}
接下來兩個實現類(對應UML類圖中ConcreteStateA):
package com.state.demo;
public class PowerOnState implements TvState {
/**
* 開機狀態下,遙控器按鈕有效
*/
@Override
public void nextChanel() {
System.out.println("---------下一頻道-----------------------");
}
@Override
public void preChanel() {
System.out.println("---------上一頻道-----------------------");
}
@Override
public void turnUp() {
System.out.println("---------音量增大-----------------------");
}
@Override
public void turnDown() {
System.out.println("---------音量減小-----------------------");
}
}
實現類2(對應UML類圖中ConcreteStateB):
package com.state.demo;
public class PowerOffState implements TvState {
/**
* 關機狀態下,所有按鈕無效
*/
@Override
public void nextChanel() {
System.out.println("---------關機狀態不可用,請先開機-----------------------");
}
@Override
public void preChanel() {
System.out.println("---------關機狀態不可用,請先開機-----------------------");
}
@Override
public void turnUp() {
System.out.println("---------關機狀態不可用,請先開機-----------------------");
}
@Override
public void turnDown() {
System.out.println("---------關機狀態不可用,請先開機-----------------------");
}
}
接下來控制類(對應UML類圖中Context):
package com.state.demo;
public class TvController {
TvState tvState=null;
public void setTvState(TvState tvState) {
this.tvState = tvState;
}
/**
* 開機
*/
public void turnOnTv(){
setTvState(new PowerOnState());
System.out.println("--------開機啦---------------");
}
/**
* 關機
*/
public void turnOffTv(){
setTvState(new PowerOffState());
System.out.println("--------關機啦---------------");
}
/**
* 下一頻道
*/
public void nextChanel(){
tvState.nextChanel();
}
public void preChanel(){
tvState.preChanel();
}
public void turnUp(){
tvState.turnUp();
}
public void turnDown(){
tvState.turnDown();
}
}
最後,編寫一個測試類,測試以上代碼:
package com.state.demo;
public class TestClass {
public static void main(String[] args) {
TvController tvController=new TvController();//創建一個控制類
tvController.turnOnTv();//首先開機
tvController.nextChanel();
tvController.turnDown();
tvController.turnOffTv();//關機
tvController.turnDown();//關機後功能不再提供
}
}
運行實例如下:

這裡有些邏輯小問題,就是在已經開機的狀態下,用戶再次調用開機要進行提示,這時我們可以在控制類中加入如下代碼;
package com.state.demo;
public class TvController {
private boolean isTvOn=false;
TvState tvState=null;
public void setTvState(TvState tvState) {
this.tvState = tvState;
}
/**
* 開機
*/
public void turnOnTv(){
if(!isTvOn){
isTvOn=true;
setTvState(new PowerOnState());
System.out.println("--------開機啦---------------");
}else{
isTvOn=true;
System.out.println("--------已經開機了,不要再按了---------------");
}
}
/**
* 關機
*/
public void turnOffTv(){
if(isTvOn){
isTvOn=false;
setTvState(new PowerOffState());
System.out.println("--------關機啦---------------");
}else{
isTvOn=false;
System.out.println("-------已經關機啦,不要再按了---------------");
}
}
/**
* 下一頻道
*/
public void nextChanel(){
tvState.nextChanel();
}
public void preChanel(){
tvState.preChanel();
}
public void turnUp(){
tvState.turnUp();
}
public void turnDown(){
tvState.turnDown();
}
}
做一個開機標識,每次調用方法之前進行判斷即可。這時再次運行測試類:
package com.state.demo;
public class TestClass {
public static void main(String[] args) {
TvController tvController=new TvController();//創建一個控制類
tvController.turnOnTv();//首先開機
tvController.turnOnTv();//首先開機
tvController.nextChanel();
tvController.turnDown();
tvController.turnOffTv();//關機
tvController.turnOffTv();//關機
tvController.turnDown();//關機後功能不再提供
}
}
運行如下:

總結:控制類持有系統狀態,但控制類不直接處理行為,行為在狀態的實現類中實現;
用戶直接操作控制類,直接和控制類交互,不直接操作狀態實現類,這樣就有一個職責的分離,有利於系統維護。
喜歡的朋友請關注我,謝謝。