狀態(tài)模式

一 定義

在軟件開發(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)多的時候律想,子類會膨脹,太多的子類绍弟,可能會不利于項目的管理技即。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市樟遣,隨后出現(xiàn)的幾起案子姥份,更是在濱河造成了極大的恐慌,老刑警劉巖年碘,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件澈歉,死亡現(xiàn)場離奇詭異,居然都是意外死亡屿衅,警方通過查閱死者的電腦和手機埃难,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涡尘,你說我怎么就攤上這事忍弛。” “怎么了考抄?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵细疚,是天一觀的道長。 經(jīng)常有香客問我川梅,道長疯兼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任贫途,我火速辦了婚禮吧彪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丢早。我一直安慰自己姨裸,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布怨酝。 她就那樣靜靜地躺著傀缩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪农猬。 梳的紋絲不亂的頭發(fā)上扑毡,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音盛险,去河邊找鬼。 笑死勋又,一個胖子當(dāng)著我的面吹牛苦掘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播楔壤,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼鹤啡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蹲嚣?” 一聲冷哼從身側(cè)響起递瑰,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎隙畜,沒想到半個月后抖部,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡议惰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年慎颗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡俯萎,死狀恐怖傲宜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情夫啊,我是刑警寧澤函卒,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站撇眯,受9級特大地震影響报嵌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叛本,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一沪蓬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧来候,春花似錦跷叉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至转质,卻和暖如春园欣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背休蟹。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工沸枯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赂弓。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓绑榴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親盈魁。 傳聞我的和親對象是個殘疾皇子翔怎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容

  • .概述 在軟件開發(fā)過程中,應(yīng)用程序可能會根據(jù)不同的情況作出不同的處理杨耙。最直接的解決方案是將這些所有可能發(fā)生的情況全...
    泥孩兒0107閱讀 375評論 0 0
  • 一赤套、電梯控制器 電梯在我們周邊隨處可見,電梯的控制邏輯中心是由電梯控制器實現(xiàn)的珊膜。電梯的控制邏輯容握,即使簡單點設(shè)計,把...
    喬治大叔閱讀 532評論 0 1
  • 參考博客iOS App的設(shè)計模式開發(fā)中對State狀態(tài)模式的運用 使用場景: 例子1:按鈕來控制一個電梯的狀態(tài)车柠,一...
    傻傻小蘿卜閱讀 742評論 0 0
  • 設(shè)計模式文章陸續(xù)更新 java單例模式j(luò)ava代理模式j(luò)ava工廠模式 狀態(tài)模式(State)-老孫七十二變 俺老...
    林銳波閱讀 1,308評論 3 9
  • 【學(xué)習(xí)難度:★★★☆☆慧域,使用頻率:★★★☆☆】直接出處:狀態(tài)模式梳理和學(xué)習(xí):https://github.com/...
    BruceOuyang閱讀 1,170評論 0 2