一 定義
在軟件開發(fā)過程中,應(yīng)用程序可能要根據(jù)不同的情況做不同的處理,最直接的解決方法是將這些情況都考慮到,然后在if...else中進行判斷学搜,來針對不同的情況做不同的處理。但是復(fù)雜情況就不好處理了论衍,隨著狀態(tài)的增加瑞佩,可能引起很大的修改,程序的可讀性坯台、擴展性都會受到影響炬丸。
定義:當(dāng)一個對象的內(nèi)在狀態(tài)改變時允許改變其行為,這個對象看起來是改變了其類。
狀態(tài)模式的核心是封裝稠炬,狀態(tài)的變更引起了行為的變更焕阿,從外部看起來就好像這個對象對應(yīng)的類發(fā)生了改變一樣。
二 模式結(jié)構(gòu)
角色介紹:
- Context:環(huán)境類首启,定義客戶端感興趣的接口暮屡,維護一個State子類的實例,這個實例定義了對象的當(dāng)前狀態(tài)毅桃。
- State:抽象狀態(tài)類或者狀態(tài)接口褒纲,定義一個或者一組接口,表示該狀態(tài)下的行為钥飞。
- ConcreteStateA莺掠、ConcreteStateB:具體狀態(tài)類,每一個具體的狀態(tài)類實現(xiàn)抽象State中定義的接口读宙,從而達到不同狀態(tài)下的不同行為汁蝶。
三 實例
我們以電梯運行為例,電梯一共四個狀態(tài)论悴,分別是開門狀態(tài),關(guān)門狀態(tài)墓律,運行狀態(tài)和停止狀態(tài)膀估。
(1)在開門狀態(tài),只能進行關(guān)門操作
(2)在關(guān)門狀態(tài)耻讽,可以進行開門察纯,運行操作
(3)在運行狀態(tài),只能進行停止操作
(4)在停止狀態(tài)针肥,可以進行運行和開門操作
我們先看第一版實現(xiàn)
- 電梯接口
電梯接口含有電梯的全部狀態(tài)和開門饼记,關(guān)門,運行慰枕,停止等幾個功能具则。
public interface ILift {
//電梯的四個狀態(tài)
int OPENING_STATE = 1; //門敞狀態(tài)
int CLOSING_STATE = 2; //門閉狀態(tài)
int RUNNING_STATE = 3; //運行狀態(tài)
int STOPPING_STATE = 4; //停止狀態(tài);
//設(shè)置電梯的狀態(tài)
public void setState(int state);
//首先電梯門開啟動作
public void open();
//電梯門有開啟具帮,那當(dāng)然也就有關(guān)閉了
public void close();
//電梯要能上能下博肋,跑起來
public void run();
//電梯還要能停下來,停不下來那就扯淡了
public void stop();
}
- 具體的電梯實現(xiàn)
public class Lift implements ILift{
private int state;
@Override
public void setState(int state) {
this.state=state;
}
// 電梯門打開
@Override
public void open() {
switch (state){
case ILift.OPENING_STATE: // 如果是打開狀態(tài)蜂厅,本來就是打開的匪凡,什么也不用做
break;
case ILift.CLOSING_STATE: // 關(guān)閉狀態(tài),可以打開
System.out.println("打開電梯門");
setState(ILift.OPENING_STATE);
break;
case ILift.RUNNING_STATE: // 運行狀態(tài)下是不能打開電梯門的
break;
case ILift.STOPPING_STATE: // 停止狀態(tài)可以打開電梯門
System.out.println("打開電梯門");
setState(ILift.OPENING_STATE);
break;
}
}
@Override
public void close() {
switch (state){
case ILift.OPENING_STATE: // 打開狀態(tài)掘猿,可以關(guān)閉
System.out.println("關(guān)閉電梯門");
setState(ILift.CLOSING_STATE);
break;
case ILift.CLOSING_STATE: // 關(guān)閉狀態(tài)病游,什么都不用做
break;
case ILift.RUNNING_STATE: // 運行狀態(tài),本來就是關(guān)閉的
break;
case ILift.STOPPING_STATE: // 停止狀態(tài),本來就是關(guān)閉的
break;
}
}
@Override
public void run() {
switch (state){
case ILift.OPENING_STATE: // 運行狀態(tài),不能開門
break;
case ILift.CLOSING_STATE: // 關(guān)閉狀態(tài)稠通,什么都不用做
break;
case ILift.RUNNING_STATE: // 運行狀態(tài),什么都不用做
break;
case ILift.STOPPING_STATE: // 運行狀態(tài)衬衬,可以停止
System.out.println("停止電梯");
setState(ILift.STOPPING_STATE);
break;
}
}
@Override
public void stop() {
switch (state){
case ILift.OPENING_STATE: // 停止狀態(tài)买猖,可以開門
System.out.println("打開電梯");
setState(ILift.OPENING_STATE);
break;
case ILift.CLOSING_STATE: // 關(guān)閉狀態(tài),什么都不用做
break;
case ILift.RUNNING_STATE: // 停止狀態(tài),可以運行
System.out.println("運行電梯");
setState(ILift.RUNNING_STATE);
break;
case ILift.STOPPING_STATE: // 停止狀態(tài)佣耐,什么都不用做
break;
}
}
}
可以看到政勃,在地電梯類的具體實現(xiàn)中,每個功能方法的實現(xiàn)都包含有switch-case語句兼砖,如果在增加幾個狀態(tài)奸远,switch-case的分支還會增多,使這個類變的越來越難以維護讽挟。
下面我們用狀態(tài)模式解決上述問題懒叛。
- 電梯的狀態(tài)接口
在該接口中定義該狀態(tài)的所有行為。
public interface ILiftState {
//首先電梯門開啟動作
public void open();
//電梯門有開啟耽梅,那當(dāng)然也就有關(guān)閉了
public void close();
//電梯要能上能下薛窥,跑起來
public void run();
//電梯還要能停下來,停不下來那就扯淡了
public void stop();
}
- 開門狀態(tài)類
開門狀態(tài)類實現(xiàn)電梯的狀態(tài)接口眼姐,并完成在此狀態(tài)下的功能诅迷。
public class OpenningState implements ILiftState{
@Override
public void open() {
}
@Override
public void close() {
System.out.println("關(guān)閉電梯門");
}
@Override
public void run() {
}
@Override
public void stop() {
}
}
- 關(guān)門狀態(tài)類
public class ClosingState implements ILiftState{
@Override
public void open() {
System.out.println("打開電梯門");
}
@Override
public void close() {
}
@Override
public void run() {
System.out.println("運行電梯");
}
@Override
public void stop() {
}
}
- 運行狀態(tài)類
public class RunningState implements ILiftState{
@Override
public void open() {
}
@Override
public void close() {
}
@Override
public void run() {
}
@Override
public void stop() {
System.out.println("停止電梯");
}
}
- 停止狀態(tài)類
public class StoppingState implements ILiftState{
@Override
public void open() {
System.out.println("停打開電梯門");
}
@Override
public void close() {
}
@Override
public void run() {
System.out.println("運行電梯");
}
@Override
public void stop() {
}
}
- 電梯遙控接口
相當(dāng)于狀態(tài)模式的Context角色。
public interface ILiftController {
public void setState(ILiftState state);
public void open();
public void close();
public void run();
public void stop();
}
- 電梯遙控具體實現(xiàn)
public class LiftController implements ILiftController{
private ILiftState mLiftState;
@Override
public void setState(ILiftState state) {
this.mLiftState=state;
}
@Override
public void open() {
mLiftState.open();
}
@Override
public void close() {
mLiftState.close();
}
@Override
public void run() {
mLiftState.run();
}
@Override
public void stop() {
mLiftState.stop();
}
}
- 測試代碼
LiftController liftController=new LiftController();
liftController.setState(new OpenningState()); // 打開電梯門
liftController.open(); // 此時不會生效
liftController.close();
liftController.run(); // 此時不會生效
liftController.stop(); // 此時不會生效
// 關(guān)閉電梯門
liftController.setState(new ClosingState());
liftController.open();
liftController.close(); // 此時不會生效
liftController.run();
liftController.stop(); // 此時不會生效
四 優(yōu)缺點
- 結(jié)構(gòu)清晰众旗,避免了過多使用switch-case或者if-else罢杉,避免了程序的復(fù)雜性。
- 遵循設(shè)計原則贡歧,每個狀態(tài)都是一個子類滩租,要想增加狀態(tài)只要增加子類即可,體現(xiàn)了開閉原則和單一職責(zé)原則利朵。
缺點:
狀態(tài)多的時候律想,子類會膨脹,太多的子類绍弟,可能會不利于項目的管理技即。