java設(shè)計(jì)模式-狀態(tài)模式(State)

定義

狀態(tài)模式,又稱為狀態(tài)對(duì)象模式(Pattern of Object for States),狀態(tài)模式是對(duì)象的行為模式。

狀態(tài)模式允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變的時(shí)候改變其行為,這個(gè)對(duì)象看上去就像是改變了它的類一樣。

狀態(tài)模式的結(jié)構(gòu)

用一句話來表述,狀態(tài)模式把所研究的對(duì)象的行為包裝在不同的狀態(tài)對(duì)象里,每一個(gè)狀態(tài)對(duì)象都屬于一個(gè)抽象狀態(tài)類的一個(gè)子類。狀態(tài)模式的意圖是讓一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變的時(shí)候,其行為也隨之改變。狀態(tài)模式的示意性類圖如下所示:

狀態(tài)模式的示意性類圖

狀態(tài)模式所涉及到的角色有:

  • 環(huán)境角色(Context):也稱上下文角色,定義客戶端所感興趣的接口,并且保留一個(gè)具體狀態(tài)類的實(shí)例。這個(gè)具體狀態(tài)類的實(shí)例給出此環(huán)境對(duì)象的現(xiàn)有狀態(tài)。
  • 抽象狀態(tài)角色(State):定義一個(gè)接口,用以封裝環(huán)境對(duì)象(Context)的一個(gè)特定的狀態(tài)所對(duì)應(yīng)的行為。
  • 具體狀態(tài)角色(ConcreteState):每一個(gè)具體狀態(tài)類都實(shí)現(xiàn)了環(huán)境對(duì)象(Context)的一個(gè)狀態(tài)所對(duì)應(yīng)的行為。

示例代碼

抽象狀態(tài)類

public interface State {
    /**
     * 狀態(tài)對(duì)應(yīng)的處理
     * @param simpleParameter
     */
    public void handle(String simpleParameter);
}

環(huán)境角色類

public class Context {
    //持有一個(gè)State類型的對(duì)象實(shí)例
    private State state;

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

    /**
     * 用戶感興趣的接口方法
     * @param simpleParameter
     */
    public void request(String simpleParameter) {
        //轉(zhuǎn)調(diào)State進(jìn)行處理
        state.handle(simpleParameter);
    }
}

具體狀態(tài)類

public class ConcreteStateA implements State {
    @Override
    public void handle(String simpleParameter) {
        System.out.print("ConcreteStateA handle : " + simpleParameter);
    }
}
public class ConcreteStateB implements State {
    @Override
    public void handle(String simpleParameter) {
        System.out.print("ConcreteStateB handle : " + simpleParameter);
    }
}

客戶端類

public class Client {
    public static void main(String[] args){
        //創(chuàng)建狀態(tài)
        State state = new ConcreteStateA();
        //創(chuàng)建環(huán)境
        Context context = new Context();
        //將狀態(tài)設(shè)置到環(huán)境中
        context.setState(state);
        //請(qǐng)求
        context.request("test");
    }
}

從上面可以看出,環(huán)境類Context的行為request()是委派給某一個(gè)具體狀態(tài)類的。通過使用多態(tài)性原則,可以動(dòng)態(tài)改變環(huán)境類Context的屬性State的內(nèi)容,使其從指向一個(gè)具體狀態(tài)類變換指向到另一個(gè)具體狀態(tài)類,從而使環(huán)境類的行為request()由不同的具體狀態(tài)類來執(zhí)行

使用場(chǎng)景

考慮一個(gè)在線投票的應(yīng)用,要實(shí)現(xiàn)控制同一個(gè)用戶只能投一票,如果一個(gè)用戶反復(fù)投票,且投票次數(shù)超過5次,則判定為惡意刷票,要取消該用戶的投票的資格,當(dāng)然同時(shí)也要取消他所投的票;如果一個(gè)用戶的投票次數(shù)超過8次,則將進(jìn)入黑名單,禁止再次登錄和使用系統(tǒng)。

要使用狀態(tài)模式實(shí)現(xiàn),首先需要把投票過程的各種狀態(tài)定義出來,根據(jù)以上描述大致分為四種狀態(tài):正常投票、反復(fù)投票、惡意刷票、進(jìn)入黑名單。然后創(chuàng)建一個(gè)投票管理對(duì)象(相當(dāng)于Context)。

系統(tǒng)的結(jié)構(gòu)圖如下所示:

在線投票系統(tǒng)結(jié)構(gòu)圖

示例代碼

抽象狀態(tài)類

public interface VoteState {
    /**
     * 處理狀態(tài)對(duì)應(yīng)的行為
     * @param user 投票人
     * @param voteItem 投票項(xiàng)
     * @param voteManager 投票上下文,用來實(shí)現(xiàn)狀態(tài)對(duì)應(yīng)的功能處理的時(shí)候,可以回調(diào)上下文的數(shù)據(jù)。
     */
    public void vote(String user, String voteItem, VoteManager voteManager);
}

具體狀態(tài)類-正常投票

public class NomalVoteState implements VoteState {
    @Override
    public void vote(String user, String voteItem, VoteManager voteManager) {
        //正常投票,記錄到投票記錄中
        voteManager.getMapVote().put(user, voteItem);
        System.out.println("恭喜投票成功!");
    }
}

具體狀態(tài)類-重復(fù)投票

public class RepeatVoteState implements VoteState {
    @Override
    public void vote(String user, String voteItem, VoteManager voteManager) {
        //重復(fù)投票,暫時(shí)不做處理
        System.out.println("請(qǐng)不要重復(fù)投票?。?!");
    }
}

具體狀態(tài)類-惡意刷票

public class SpiteVoteState implements VoteState {
    @Override
    public void vote(String user, String voteItem, VoteManager voteManager) {
        //惡意投票,取消用戶的投票資格,并且取消用戶的投票
        String str = voteManager.getMapVote().get(user);
        if (str != null) {
            voteManager.getMapVote().remove(user);
        }
        System.out.println("你有惡意刷票行為,取消投票資格?。?!");
    }
}

具體狀態(tài)類-黑名單

public class BlackVoteState implements VoteState {
    @Override
    public void vote(String user, String voteItem, VoteManager voteManager) {
        //記入黑名單,禁止登錄系統(tǒng)
        System.out.println("記入黑名單,將禁止登錄和使用本系統(tǒng)!?。?);
    }
}

環(huán)境類

public class VoteManager {
    //持有狀態(tài)處理對(duì)象
    private VoteState state;
    //記錄用戶投票的結(jié)果,Map<String, String>對(duì)應(yīng)Map<用戶名稱, 投票的選項(xiàng)>
    private Map<String, String> mapVote = new HashMap<>();
    //記錄用戶投票次數(shù),Map<String, String>對(duì)應(yīng)Map<用戶名稱, 投票次數(shù)>
    private Map<String, Integer> mapVoteCount = new HashMap<>();

    /**
     * 獲取用戶投票結(jié)果的map
     * @return 返回投票結(jié)果Map
     */
    public Map<String, String> getMapVote() {
        return mapVote;
    }

    /**
     * 投票
     * @param user 投票人
     * @param voteItem 投票選項(xiàng)
     */
    public void vote(String user, String voteItem) {
        //1. 為該用戶增加投票次數(shù)
        //獲取用戶當(dāng)前的投票次數(shù),如果沒有,則默認(rèn)為0
        Integer oldVoteCount = mapVoteCount.get(user);
        if (oldVoteCount == null) {
            oldVoteCount = 0;
        }
        //增加并更新投票次數(shù)
        oldVoteCount += 1;
        mapVoteCount.put(user, oldVoteCount);

        //2.判斷用戶的投票類型,也就是判斷狀態(tài)
        //判斷是正常投票、重復(fù)投票、惡意投票還是已經(jīng)記錄黑名單的狀態(tài)
        if (oldVoteCount == 1) {
            state = new NomalVoteState();
        } else if (oldVoteCount > 1 && oldVoteCount < 5) {
            state = new RepeatVoteState();
        } else if (oldVoteCount >= 5 && oldVoteCount < 8) {
            state = new SpiteVoteState();
        } else if (oldVoteCount > 0){
            state = new BlackVoteState();
        }
        //然后轉(zhuǎn)掉狀態(tài)對(duì)象來進(jìn)行相應(yīng)的操作
        state.vote(user, voteItem, this);
    }
}

客戶端類

public class Client {
    public static void main(String[] args) {
        VoteManager manager = new VoteManager();
        for (int i = 0; i < 9; i++) {
            manager.vote("Sschen", "A");
        }
    }
}

執(zhí)行結(jié)果如下

恭喜投票成功!
請(qǐng)不要重復(fù)投票?。?!
請(qǐng)不要重復(fù)投票?。?!
請(qǐng)不要重復(fù)投票!??!
你有惡意刷票行為,取消投票資格?。?!
你有惡意刷票行為,取消投票資格?。?!
你有惡意刷票行為,取消投票資格?。?!
記入黑名單,將禁止登錄和使用本系統(tǒng)?。。?記入黑名單,將禁止登錄和使用本系統(tǒng)?。?!

從上面的示例可以看出,狀態(tài)的轉(zhuǎn)換基本上都是內(nèi)部行為,主要在狀態(tài)模式內(nèi)部來維護(hù)。比如對(duì)于投票的人員,任何時(shí)候他的操作都是投票,但是投票管理對(duì)象的處理卻不一定一樣,會(huì)根據(jù)投票的次數(shù)來判斷狀態(tài),然后根據(jù)狀態(tài)去選擇不同的處理。

認(rèn)識(shí)狀態(tài)模式

狀態(tài)和行為

所謂對(duì)象的狀態(tài),通常指的就是對(duì)象實(shí)例的屬性的值;而行為指的就是對(duì)象的功能,在具體一點(diǎn)說,行為大多可以對(duì)應(yīng)到方法上。

狀態(tài)模式的功能就是分離狀態(tài)的行為,通過維護(hù)狀態(tài)的變化,來調(diào)用不同狀態(tài)對(duì)應(yīng)的不同功能。也就是說,狀態(tài)和行為是相關(guān)聯(lián)的,他們的關(guān)系可以描述為:狀態(tài)決定行為

由于狀態(tài)是在運(yùn)行期間被改變的,因此行為也會(huì)在運(yùn)行期間根據(jù)狀態(tài)的改變而改變。

行為的平行性

注意是平行性而不是平等性。所謂平行性指的是各種狀態(tài)的行為所處的層次是一樣的、相互獨(dú)立的、沒有關(guān)聯(lián)的。運(yùn)行中根據(jù)不同的狀態(tài)來決定到底走平行線的哪一條。行為是不同的,當(dāng)然對(duì)應(yīng)的實(shí)現(xiàn)也是不同的,相互之間是不可替換的。

狀態(tài)的平行性示意圖

而平等性強(qiáng)調(diào)的是可替換性,大家是同一行為的不同描述或?qū)崿F(xiàn),因此在同一個(gè)行為發(fā)生的時(shí)候,可以根據(jù)條件挑選任意一個(gè)實(shí)現(xiàn)來進(jìn)行相應(yīng)的處理。

平等性示意圖

由此大家可以發(fā)現(xiàn)狀態(tài)模式的結(jié)構(gòu)和策略模式的結(jié)構(gòu)完全一樣,但是,她們的目的、實(shí)現(xiàn)
、本質(zhì)確是完全不一樣的。還有行為之間的特性也是狀態(tài)模式和策略模式一個(gè)很重要的區(qū)別,狀態(tài)模式的行為是平行性的,不可相互替換的;而策略模式的行為是平等性的,是可以相互替換的。

環(huán)境和狀態(tài)處理對(duì)象

在狀態(tài)模式中,環(huán)境Context是持有狀態(tài)的對(duì)象,但是環(huán)境Context自身并不處理跟狀態(tài)相關(guān)的行為,而是把處理狀態(tài)的功能委托給了相應(yīng)的狀態(tài)處理類來處理。

在具體的狀態(tài)處理類中經(jīng)常需要獲取環(huán)境Context自身的數(shù)據(jù),甚至在必要的時(shí)候會(huì)回調(diào) 環(huán)境Context的方法。因此,通常將環(huán)境Context自身當(dāng)作一個(gè)參數(shù)傳遞給具體的狀態(tài)處理類。

客戶端一般只和環(huán)境Context交互??蛻舳丝梢杂脿顟B(tài)對(duì)象來配置一個(gè)環(huán)境Context,一旦配置完畢,就不再需要和狀態(tài)對(duì)象打交道了??蛻舳送ǔ2回?fù)責(zé)運(yùn)行期間狀態(tài)的維護(hù),也不負(fù)責(zé)決定后續(xù)到底使用哪一種具體的狀態(tài)處理對(duì)象。

參考

《JAVA與模式》之狀態(tài)模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 場(chǎng)景問題# 1.1 實(shí)現(xiàn)在線投票## 考慮一個(gè)在線投票的應(yīng)用,要實(shí)現(xiàn)控制同一個(gè)用戶只能投一票,如果一個(gè)用戶反復(fù)...
    七寸知架構(gòu)閱讀 2,050評(píng)論 7 53
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,820評(píng)論 11 349
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 4,094評(píng)論 1 15
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,654評(píng)論 19 139
  • “這是什么顏色呀?” “這是紅色。咦,這有一輛紅色的小汽車,小汽車?yán)锩嬗兄话咨男⊥米?,小兔子還會(huì)開小汽車哦。這還...
    橙子花兒開閱讀 267評(píng)論 0 1

友情鏈接更多精彩內(nèi)容