Java 設(shè)計(jì)模式(12) —— 狀態(tài)模式

一柄错、狀態(tài)模式

能根據(jù)內(nèi)部狀態(tài)的變化埃碱,改變對(duì)象的行為嚎尤,看起來好像修改了類

狀態(tài)模式

二荔仁、示例

智能糖果機(jī):需要設(shè)計(jì)一款自助購買的糖果機(jī),糖果機(jī)的狀態(tài)有

  • 準(zhǔn)備使用(接下來可投入硬幣)
  • 投入硬幣(接下來可搖動(dòng)把手或者退出硬幣)
  • 售出糖果(接下來可恢復(fù)使用或者售罄)
  • 售罄狀態(tài)

使用傳統(tǒng)的面向?qū)ο竽J脚灯唬恍瓒x一個(gè)糖果機(jī)的對(duì)象咕晋,內(nèi)部根據(jù)不同的狀態(tài)進(jìn)行不同的操作處理


/**
 * 面向?qū)ο竽J剑枪麢C(jī)
 */
public class CandyMachine {

    final static int SoldOutState = 0;          // 售罄狀態(tài)
    final static int OnReadyState = 1;          // 準(zhǔn)備售出狀態(tài)
    final static int HasCoin = 2;               // 已經(jīng)投了硬幣的狀態(tài)
    final static int SoldState = 3;             // 售出狀態(tài)

    private int state = SoldOutState;
    private int count = 0;                      // 內(nèi)部糖果的剩余數(shù)量

    public CandyMachine(int count) {
        this.count = count;
        if (count > 0) {
            state = OnReadyState;
        }
    }

    /**
     * 投入硬幣收奔,如果糖果機(jī)狀態(tài)是準(zhǔn)備售出掌呜,則可以搖動(dòng)把手購買糖果,其他的狀態(tài)則無法使用
     */
    public void insertCoin() {
        switch (state) {
            case SoldOutState:
                System.out.println("you can't insert coin,the machine sold out!");
                break;
            case OnReadyState:
                state = HasCoin;
                System.out
                        .println("you have inserted a coin,next,please turn crank!");
                break;
            case HasCoin:
                System.out.println("you can't insert another coin!");

                break;
            case SoldState:
                System.out.println("please wait!we are giving you a candy!");

                break;
        }

    }

    /**
     * 如果已經(jīng)投入硬幣之后則可以選擇退出硬幣
     */
    public void returnCoin() {
        switch (state) {
            case SoldOutState:
                System.out
                        .println("you can't return,you haven't inserted a coin yet!");
                break;
            case OnReadyState:
                System.out.println("you haven't inserted a coin yet!");
                break;
            case HasCoin:

                System.out.println("coin return!");
                state = OnReadyState;

                break;
            case SoldState:
                System.out.println("sorry,you already have turned the crank!");

                break;
        }

    }

    /**
     * 如果已經(jīng)投入硬幣則可以搖動(dòng)把手
     */
    public void turnCrank() {
        switch (state) {
            case SoldOutState:
                System.out.println("you turned,but there are no candies!");
                break;
            case OnReadyState:
                System.out.println("you turned,but you haven't inserted a coin!");
                break;
            case HasCoin:
                System.out.println("crank turn...!");
                state = SoldState;
                dispense();
                break;
            case SoldState:
                System.out.println("we are giving you a candy,turning another get nothing,!");
                break;
        }

    }

    /**
     * 掉出糖果
     */
    private void dispense() {
        count--;
        System.out.println("a candy rolling out!");
        if (count > 0) {
            state = OnReadyState;
        } else {
            System.out.println("Oo,out of candies");
            state = SoldOutState;
        }

    }

    public void printstate() {

        switch (state) {
            case SoldOutState:
                System.out.println("***SoldOutState***");
                break;
            case OnReadyState:
                System.out.println("***OnReadyState***");
                break;
            case HasCoin:
                System.out.println("***HasCoin***");

                break;
            case SoldState:
                System.out.println("***SoldState***");
                break;
        }

    }
}

缺點(diǎn):各種行為耦合度較高坪哄,不利于新需求的拓展

三质蕉、狀態(tài)模式改進(jìn)

雖然傳統(tǒng)的面向?qū)ο竽J揭部蓾M足功能,但是若新增需求則不利于拓展翩肌。加入此項(xiàng)目新增一個(gè)幸運(yùn)者的功能模暗,售出糖果的時(shí)候有一定幾率成為幸運(yùn)者掉出兩顆糖果。

若滿足該功能念祭,則需在原本完成的糖果機(jī)代碼中修改兑宇,則易造成其他不必要的bug,則引出狀態(tài)模式:

  • 定義一個(gè)狀態(tài)接口粱坤,接口定義需要處理的方法
  • 定義不同狀態(tài)的接口對(duì)象隶糕,對(duì)象內(nèi)實(shí)現(xiàn)不同狀態(tài)對(duì)不同操作的處理

/**
 * 狀態(tài)模式,統(tǒng)一的狀態(tài)接口站玄,接口內(nèi)實(shí)現(xiàn)不同狀態(tài)下不同的處理行為
 */
public interface State {

    // 投入硬幣
    public void insertCoin();

    // 退出硬幣
    public void returnCoin();

    // 搖動(dòng)把手
    public void turnCrank();

    // 掉出糖果
    public void dispense();

    // 打印當(dāng)前狀態(tài)
    public void printstate();
}

/**
 * 狀態(tài)模式枚驻,新增的幸運(yùn)者狀態(tài),出糖果的時(shí)候有一定概率出兩顆
 */
public class WinnerState implements State {

    private CandyMachine mCandyMachine;

    public WinnerState(CandyMachine mCandyMachine) {
        this.mCandyMachine = mCandyMachine;
    }

    @Override
    public void insertCoin() {
        System.out.println("please wait!we are giving you a candy!");

    }

    @Override
    public void returnCoin() {
        System.out.println("you haven't inserted a coin yet!");

    }

    @Override
    public void turnCrank() {
        System.out.println("we are giving you a candy,turning another get nothing,!");

    }

    @Override
    public void dispense() {
        mCandyMachine.releaseCandy();
        if (mCandyMachine.getCount() == 0) {
            mCandyMachine.setState(mCandyMachine.mSoldOutState);
        } else {
            System.out.println("you are a winner!you get another candy!");
            mCandyMachine.releaseCandy();
            if (mCandyMachine.getCount() > 0) {
                mCandyMachine.setState(mCandyMachine.mOnReadyState);
            } else {
                System.out.println("Oo,out of candies");
                mCandyMachine.setState(mCandyMachine.mSoldOutState);
            }
        }

    }

    @Override
    public void printstate() {
        System.out.println("***WinnerState***");
    }

}

/**
 * 狀態(tài)模式株旷,糖果機(jī)內(nèi)只定義狀態(tài)接口對(duì)象再登,在接口內(nèi)部給糖果機(jī)的狀態(tài)賦值
 */
public class CandyMachine {

    State mSoldOutState;
    State mOnReadyState;
    State mHasCoin;
    State mSoldState;
    State mWinnerState;
    private State state;
    private int count = 0;

    public CandyMachine(int count) {
        this.count = count;
        mSoldOutState = new SoldOutState(this);
        mOnReadyState = new OnReadyState(this);
        mHasCoin = new HasCoin(this);
        mSoldState = new SoldState(this);
        mWinnerState = new WinnerState(this);
        if (count > 0) {
            state = mOnReadyState;
        } else {
            state = mSoldOutState;
        }
    }

    public void setState(State state) {
        this.state = state;
    }

    /**
     * 糖果機(jī)的一切狀態(tài)都直接調(diào)用狀態(tài)接口內(nèi)部的方法
     */
    public void insertCoin() {
        state.insertCoin();
    }

    public void returnCoin() {
        state.returnCoin();
    }

    public void turnCrank() {
        state.turnCrank();
        state.dispense();
    }

    void releaseCandy() {
        if (count > 0) {
            count = count - 1;
            System.out.println("a candy rolling out!");
        }

    }

    public int getCount() {
        return count;
    }

    public void printstate() {
        state.printstate();
    }
}

/**
 * 狀態(tài)模式,通過定義一個(gè)狀態(tài)接口晾剖,接口內(nèi)實(shí)現(xiàn)不同狀態(tài)下不同的處理方式
 * 則調(diào)用處理方式時(shí)直接調(diào)用狀態(tài)接口對(duì)象的實(shí)現(xiàn)方法
 * 使用狀態(tài)模式可以是不同狀態(tài)接互不影響锉矢,也可便于新增多種狀態(tài)類型的需求
 */
public class MainTest {
    public static void main(String[] args) {
        CandyMachine mCandyMachine = new CandyMachine(6);

        mCandyMachine.printstate();

        mCandyMachine.insertCoin();
        mCandyMachine.printstate();

        mCandyMachine.turnCrank();

        mCandyMachine.printstate();

        mCandyMachine.insertCoin();
        mCandyMachine.printstate();

        mCandyMachine.turnCrank();

        mCandyMachine.printstate();
    }
}

四、總結(jié)

在很多情況下齿尽,一個(gè)對(duì)象的行為取決于一個(gè)或多個(gè)動(dòng)態(tài)變化的屬性沈撞,這樣的屬性叫做狀態(tài),這樣的對(duì)象叫做有狀態(tài)的(stateful)對(duì)象雕什,這樣的對(duì)象狀態(tài)是從事先定義好的一系列值中取出的缠俺。當(dāng)一個(gè)這樣的對(duì)象與外部事件產(chǎn)生互動(dòng)時(shí),其內(nèi)部狀態(tài)就會(huì)改變贷岸,從而使得系統(tǒng)的行為也隨之發(fā)生變化壹士。


Java設(shè)計(jì)模式所有示例代碼,持續(xù)更新中

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末偿警,一起剝皮案震驚了整個(gè)濱河市躏救,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌螟蒸,老刑警劉巖盒使,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異七嫌,居然都是意外死亡少办,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門诵原,熙熙樓的掌柜王于貴愁眉苦臉地迎上來英妓,“玉大人,你說我怎么就攤上這事绍赛÷溃” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵吗蚌,是天一觀的道長(zhǎng)腿倚。 經(jīng)常有香客問我,道長(zhǎng)蚯妇,這世上最難降的妖魔是什么敷燎? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮侮措,結(jié)果婚禮上懈叹,老公的妹妹穿的比我還像新娘。我一直安慰自己分扎,他們只是感情好澄成,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著畏吓,像睡著了一般墨状。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上菲饼,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天肾砂,我揣著相機(jī)與錄音,去河邊找鬼宏悦。 笑死镐确,一個(gè)胖子當(dāng)著我的面吹牛包吝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播源葫,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼诗越,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了息堂?” 一聲冷哼從身側(cè)響起嚷狞,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎荣堰,沒想到半個(gè)月后床未,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡振坚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年薇搁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屡拨。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡只酥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出呀狼,到底是詐尸還是另有隱情裂允,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布哥艇,位于F島的核電站绝编,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏貌踏。R本人自食惡果不足惜十饥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祖乳。 院中可真熱鬧逗堵,春花似錦、人聲如沸眷昆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亚斋。三九已至作媚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間帅刊,已是汗流浹背纸泡。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赖瞒,地道東北人女揭。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓蚤假,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親田绑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子勤哗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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

  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說閱讀 10,928評(píng)論 6 13
  • 今天我們來做一個(gè)糖果機(jī)吧,用戶只需要投入25美分掩驱,就可以購買糖果了,具體的構(gòu)造如下圖所示: 每個(gè)圓圈都表示一種狀態(tài)...
    西木柚子閱讀 1,061評(píng)論 1 11
  • 標(biāo)簽: python 設(shè)計(jì)模式 引子 狀態(tài)模式確實(shí)很好玩冬竟,我是說書上的例子確實(shí)很好玩欧穴,我對(duì)著電腦玩了好長(zhǎng)時(shí)間,但是...
    plectrum閱讀 2,802評(píng)論 0 7
  • 作者:康蘭英 指導(dǎo)老師:劉艷 袁浩 鄭鵬 內(nèi)容:關(guān)鍵詞的提煉是導(dǎo)圖的關(guān)鍵泵殴。于是我又整理一遍涮帘。中心圖用鑰匙去表達(dá)主題...
    杏花杏子?jì)?/span>閱讀 135評(píng)論 0 0
  • 今天想分享下昨天的一點(diǎn)小收獲。 放假這幾天把時(shí)間都給了女兒笑诅,竟然松懈了平時(shí)的學(xué)習(xí)計(jì)劃调缨,直到昨天想要改變。 首先列好...
    快快媽媽育兒說閱讀 218評(píng)論 1 0