1. 設(shè)計(jì)模式詳解——狀態(tài)模式

        共 3237字,需瀏覽 7分鐘

         ·

        2021-10-25 01:14

        前言

        今天我們來看一個(gè)號稱策略模式雙胞胎的設(shè)計(jì)模式——狀態(tài)模式,如它的名字一樣,狀態(tài)模式最核心的設(shè)計(jì)思路就是將對象的狀態(tài)抽象出一個(gè)接口,然后根據(jù)它的不同狀態(tài)封裝其行為,這樣就可以實(shí)現(xiàn)狀態(tài)和行為的綁定,最終實(shí)現(xiàn)對象和狀態(tài)的有效解耦。下面我們就來詳細(xì)看下它的基本原理和實(shí)現(xiàn)過程吧。

        狀態(tài)模式

        狀態(tài)模式允許對象在內(nèi)部狀態(tài)改變時(shí)改變它的行為,對象看起來好像修改了它的類。

        要點(diǎn)

        • 狀態(tài)模式允許一個(gè)對象基于內(nèi)部狀態(tài)而擁有不同的行為
        • 和程序狀態(tài)機(jī)(PSM)不同,狀態(tài)模式用類代表狀態(tài)
        • Context會(huì)將行為委托給當(dāng)前狀態(tài)對象
        • 通過將每個(gè)狀態(tài)封裝進(jìn)一個(gè)類,我們把以后需要做的任何改變局部化了
        • 狀態(tài)模式和策略模式有相同的類圖,但是它們的意圖不同
        • 策略模式通常會(huì)用行為或算法來配置Context
        • 狀態(tài)模式允許Context隨著狀態(tài)的改變而改變行為
        • 狀態(tài)轉(zhuǎn)換可以由State類或Context類控制
        • 使用狀態(tài)模式通常會(huì)導(dǎo)致設(shè)計(jì)中類的數(shù)目大量增加
        • 狀態(tài)類可以被多個(gè)Context示例共享

        優(yōu)缺點(diǎn)

        優(yōu)點(diǎn)
        1. 封裝了轉(zhuǎn)換規(guī)則。

        2. 枚舉可能的狀態(tài),在枚舉狀態(tài)之前需要確定狀態(tài)種類。

        3. 將所有與某個(gè)狀態(tài)有關(guān)的行為放到一個(gè)類中,并且可以方便地增加新的狀態(tài),只需要改變對象狀態(tài)即可改變對象的行為。

        4. 允許狀態(tài)轉(zhuǎn)換邏輯與狀態(tài)對象合成一體,而不是某一個(gè)巨大的條件語句塊

        5. 可以讓多個(gè)環(huán)境對象共享一個(gè)狀態(tài)對象,從而減少系統(tǒng)中對象的個(gè)數(shù)。

        缺點(diǎn)
        1. 狀態(tài)模式的使用必然會(huì)增加系統(tǒng)類和對象的個(gè)數(shù)。
        2. 狀態(tài)模式的結(jié)構(gòu)與實(shí)現(xiàn)都較為復(fù)雜,如果使用不當(dāng)將導(dǎo)致程序結(jié)構(gòu)和代碼的混亂。
        3. 狀態(tài)模式對"開閉原則"的支持并不太好,對于可以切換狀態(tài)的狀態(tài)模式,增加新的狀態(tài)類需要修改那些負(fù)責(zé)狀態(tài)轉(zhuǎn)換的源代碼,否則無法切換到新增狀態(tài),而且修改某個(gè)狀態(tài)類的行為也需修改對應(yīng)類的源代碼。

        使用場景

        1. 行為隨狀態(tài)改變而改變的場景。
        2. 條件、分支語句的代替者。

        示例

        狀態(tài)接口

        首先是狀態(tài)接口,這個(gè)接口是給我們實(shí)際的狀態(tài)對象繼承的,這個(gè)接口有一個(gè)方法doAction,這個(gè)方法就是給不同的狀態(tài)對象實(shí)現(xiàn)的,用于處理不同狀態(tài)下的行為的。

        public?interface?State?{
        ????/**
        ?????*?改變狀態(tài)的操作
        ?????*?@param?context
        ?????*/

        ????void?doAction(Context?context);
        }
        狀態(tài)所屬者

        然后是我們的狀態(tài)所屬者,這個(gè)類有一個(gè)核心的屬性就是我們的State接口。

        public?class?Context?{
        ????private?State?state;

        ????public?Context(){}

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

        ????public?State?getState(){
        ????????return?state;
        ????}
        ?????@Override
        ????public?String?toString()?{
        ????????return?"Context{"?+
        ????????????????"state="?+?state?+
        ????????????????'}';
        ????}
        }
        狀態(tài)實(shí)現(xiàn)

        狀態(tài)實(shí)現(xiàn)者繼承了State接口,并實(shí)現(xiàn)了doAction方法,在方法內(nèi)部可以對我們的狀態(tài)所有者進(jìn)行對應(yīng)的操作。

        這里是一個(gè)啟動(dòng)狀態(tài):

        public?class?StopState?implements?State?{

        ????private?String?name;

        ????public?StopState()?{
        ????????this.name?=?"stop";
        ????}

        ????@Override
        ????public?void?doAction(Context?context)?{
        ????????System.out.println("Context?is?in?stop?state");
        ????????context.setState(this);
        ????????System.out.println(context);
        ????}

        ????@Override
        ????public?String?toString()?{
        ????????return?"StopState{"?+
        ????????????????"name='"?+?name?+?'\''?+
        ????????????????'}';
        ????}
        }

        這里是停止?fàn)顟B(tài)

        public?class?StartState?implements?State{

        ????private?String?name;

        ????public?StartState()?{
        ????????this.name?=?"start";
        ????}

        ????@Override
        ????public?void?doAction(Context?context)?{
        ????????System.out.println("Context?is?in?start?state");
        ????????context.setState(this);
        ????????System.out.println(context);
        ????}

        ????@Override
        ????public?String?toString()?{
        ????????return?"StartState{"?+
        ????????????????"name='"?+?name?+?'\''?+
        ????????????????'}';
        ????}
        }
        測試代碼

        這里分別實(shí)例化了容器和狀態(tài)的示例,然后通過示例的doAction方法操作容器

        @Test
        ????public?void?testState()?{
        ????????Context?context?=?new?Context();

        ????????StartState?startState?=?new?StartState();
        ????????startState.doAction(context);


        ????????StopState?stopState?=?new?StopState();
        ????????stopState.doAction(context);

        ????}
        運(yùn)行結(jié)果

        可以看到,狀態(tài)對象的doAction方法執(zhí)行后,容器對應(yīng)的狀態(tài)也發(fā)生了改變:

        好了,關(guān)于狀態(tài)模式就先說這么多,接下來我們做一個(gè)簡單的總結(jié)。

        總結(jié)

        有用過策略模式或者對策略模式比較熟悉的小伙伴應(yīng)該發(fā)現(xiàn)了:策略模式其實(shí)和我們今天分析狀態(tài)模式特別像,甚至連架構(gòu)模式都是一樣的,所以這里我們有必要說下它們的區(qū)別。

        首先是策略模式,它其實(shí)是將不同的算法封裝成不同的策略,然后在具體的策略中實(shí)現(xiàn)具體的行為,但是測試本身是被動(dòng)被選擇的,容器選擇策略,調(diào)用過程發(fā)生在容器中,而且策略本身是入?yún)ⅲ?/p>

        而我們今天分析的狀態(tài)模式,它是將不同狀態(tài)對應(yīng)的行為封裝,然后由具體的狀態(tài)操作容器,整個(gè)過程更像是狀態(tài)主動(dòng)發(fā)起的,由狀態(tài)執(zhí)行其自己的方法,入?yún)⑹侨萜鳌?/p>

        這兩種設(shè)計(jì)模式從某種程度上說是可以互相替換的,但是還是要結(jié)合具體業(yè)務(wù)分析的,比如spring boot啟動(dòng)過程中,它用到的就是狀態(tài)模式,這一點(diǎn)我們在分析spring boot啟動(dòng)過程中也發(fā)現(xiàn)了;但如果是涉及到算法層面的內(nèi)容,比如兩個(gè)數(shù)的加減乘除,顯然策略模式才是更好的選擇。

        總之,學(xué)習(xí)設(shè)計(jì)模式除了要了解它的基本原理和應(yīng)用場景之外,更重要的是,要學(xué)會(huì)辨識優(yōu)秀框架中的設(shè)計(jì)模式(知識,知就是知道,了解,識就是辨識,分析),最終將這些設(shè)計(jì)模式應(yīng)用到我們的業(yè)務(wù)開發(fā)之中。

        - END -


        瀏覽 47
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 欧美草比 | 国产在线精品一区二区不卡了阿恩 | 亚洲第一页在线视频 | 少妇一级1淫片 | 黄色一级网 |