(《設計模式解析與實戰》讀書筆記)
一、定義
當一個對象的內在狀態改變時允許改變其行爲,這個對象看起來像是改變了其類。也就是說行爲由狀態決定,不同的狀態下有不同的行爲。
二、使用場景
(1)一個對象的行爲取決於它的狀態,並且它必須在運行時根據狀態改變它的行爲;
(2)代碼中包含大量與對象狀態有關的條件語句。例如,一個操作中含有龐大的多分支語句,且這些分支依賴於該對象的狀態。
三、簡單示例
比如說電視有開關機兩種狀態,遙控器上的功能根據電視機狀態不同,相應的表現出不同的行爲,若電視關機,那麼除了開機鍵以外,其他鍵都不管用,而開機後,重複按開機鍵,開機鍵表現無效。
未使用狀態模式的代碼:
/**
* 電視遙控器。包括開機、關機、上一頻道、下一頻道、增減音量
*/
public class TVController {
private final static int POWER_ON = 1;
private final static int POWER_OFF = 2;
private int mState = POWER_OFF;
public void powerOn() {
mState = POWER_ON;
if (mState == POWER_OFF) {
System.out.println("開機");
}
}
public void powerOff() {
mState = POWER_OFF;
if (mState == POWER_ON) {
System.out.println("關機");
}
}
public void nextChannel() {
if (mState == POWER_ON) {
System.out.println("下一頻道");
}else {
System.out.println("請先開機");
}
}
public void prevChannel() {
if (mState == POWER_ON) {
System.out.println("上一頻道");
}else {
System.out.println("請先開機");
}
}
public void turnUp() {
if (mState == POWER_ON) {
System.out.println("調高音量");
}else {
System.out.println("請先開機");
}
}
public void turnDown() {
if (mState == POWER_ON) {
System.out.println("調低音量");
}else {
System.out.println("請先開機");
}
}
}
可以看出上面的代碼使用了大量的if-else語句,代碼顯得臃腫。
使用狀態模式解決這個問題:
/**
* 電視狀態接口,定義了電視操作的函數
*/
public interface TVState {
/**
* 下一頻道
*/
public void nextChannel();
/**
* 上一頻道
*/
public void prevChannel();
/**
* 調高音量
*/
public void turnUp();
/**
* 調低音量
*/
public void turnDown();
}
/**
* 開機狀態,此時再觸發開機功能不做任何操作
*/
public class PowerOnState implements TVState{
@Override
public void nextChannel() {
System.out.println("下一頻道");
}
@Override
public void prevChannel() {
System.out.println("上一頻道");
}
@Override
public void turnUp() {
System.out.println("調高音量");
}
@Override
public void turnDown() {
System.out.println("調低音量");
}
}
/**
* 關機狀態,此時只有開機功能是有效的
*/
public class PowerOffState implements TVState{
@Override
public void nextChannel() {
}
@Override
public void prevChannel() {
}
@Override
public void turnUp() {
}
@Override
public void turnDown() {
}
}
/**
* 電源操作接口
*/
public interface PowerController {
/**
* 開機
*/
public void powerOn();
/**
* 關機
*/
public void powerOff();
}
/**
* 電視遙控器
*/
public class TVcontrol implements PowerController {
TVState mTVState;
public void setTVState(TVState tVState) {
this.mTVState = tVState;
}
@Override
public void powerOn() {
setTVState(new PowerOnState());
System.out.println("開機");
}
@Override
public void powerOff() {
setTVState(new PowerOffState());
System.out.println("關機");
}
public void nextChannel() {
mTVState.nextChannel();
}
public void prevChannel() {
mTVState.prevChannel();
}
public void turnUp() {
mTVState.turnUp();
}
public void turnDown() {
mTVState.turnDown();
}
}
/**
* 客戶端調用
*/
public class Client {
public static void main(String[] args) {
TVcontrol tvControl = new TVcontrol();
// 設置爲開機狀態
tvControl.powerOn();
tvControl.nextChannel();
tvControl.prevChannel();
tvControl.turnUp();
tvControl.turnDown();
System.out.println();
// 設置爲關機狀態
tvControl.powerOff();
tvControl.nextChannel();
tvControl.prevChannel();
tvControl.turnUp();
tvControl.turnDown();
}
}
運行結果:
四、優缺點
優點:
狀態模式將所有與一個特定的狀態有關的的行爲都放入一個狀態對象中,它提供了一個更好的方法來組織與特定狀態相關的代碼,將繁瑣的狀態判斷轉換成結構清晰的狀態類族,在避免代碼膨脹的同時也保證了可擴展性與可維護性。
缺點:
狀態模式的使用必然會增加系統類和對象的個數。