Java設計模式之狀態模式

狀態模式(State pattern): 允許對象在內部狀態改變時改變它的行爲, 對象看起來好象改了它的類。

狀態模式的詳解

簡單描述,狀態模式把所研究對象的行文包裝在不同的狀態對象裏,所有的狀態對象都是抽象狀態的子類。狀態模式的目的就是一個狀態對象在其內部改變的時候,此狀態對象的行爲也隨之改變。

類圖:

這裏寫圖片描述

角色說明:
抽象狀態角色(State):定義一個接口,用以封裝環境(Context)對象的一個特定的狀態所對應的行爲。

具體狀態角色(ConcreteState):每一個具體狀態都實現了環境(Context)的一個狀態所對應的行爲。

環境角色(Context):也是上下文意思,定義客戶端所感興趣的接口,並且保留一個具體狀態的實例。這個具體狀態類的實例給出此環境對象的現有狀態。

代碼演示,抽象狀態類:

public interface State {
    /**
     * 狀態對應的改變
     */
    public void change(String sampleParameter);
}

環境狀態類:

public class Context {
    // 持有一個State類型的對象實例
    private State state;

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

    /**
     * 用戶感興趣的接口方法
     */
    public void request(String sampleParameter) {
        // 轉調state來處理
        state.change(sampleParameter);
    }
}

具體狀態類1:

public class ConcreteState1 implements State {

    @Override
    public void change(String sampleParameter) {

        System.out.println("具體狀態類1進行行爲改變:" + sampleParameter);
    }

}

具體狀態類2:

public class ConcreteState2 implements State {

    @Override
    public void change(String sampleParameter) {

        System.out.println("具體狀態類2進行行爲改變:" + sampleParameter);
    }

}

客戶端測試類:

public class Client {

    public static void main(String[] args) {
        // 創建狀態
        State state = new ConcreteState1();
        // 創建環境
        Context context = new Context();
        // 將狀態設置到環境中
        context.setState(state);
        // 請求
        context.request("Holle world");
    }
}

運行結果:
這裏寫圖片描述

從上面的例子可以看出,環境狀態類裏面是通過行爲request()來委派一個具體狀態類來改變的。通過多態性原則,我們可以隨意改變環境狀態類裏面的state屬性,使其可以變換的指向任意一個具體狀態角色,從而使環境狀態類的行爲request()裏面由不同的具體狀態類來執行。

適用場景
一個在線投票系統,要實現控制同一個用戶只能投一次票。如果同一個用戶投一次票是投票成功;再投就記爲重複投票;超過5次就記爲惡意刷票,將取消此用戶的投票記錄;超過8次就記爲黑名單,禁止登陸和使用本系統。

用狀態模式來實現,抽象狀態角色(State)就是投票的狀態,而具體狀態角色(ConcreteState)就有:正常投票、反覆投票、惡意刷票、進入黑名單。再創建一個投票管理的對象,這個就相當於環境狀態角色(Context)。

類圖:

這裏寫圖片描述

代碼演示,抽象的投票狀態類:

public interface VoteState {
    /**
     * 狀態對應行爲的處理
     * 
     * @param user
     *            投票人
     * @param voteItem
     *            投票項
     * @param voteManager
     *            投票上下文,當需要改變投票狀態時可以回調上下文數據
     */
    public void vote(String user, String voteItem, VoteManager voteManager);
}

正常投票類:

public class NormalVoteState implements VoteState {

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

}

重複投票類:

public class RepeatVoteState implements VoteState {

    @Override
    public void vote(String user, String voteItem, VoteManager voteManager) {
        // 重複投票,暫時不做處理
        System.out.println(user + ",請不要重複投票");
    }

}

惡意投票類:

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(user + ",你有惡意刷屏行爲,取消投票資格");
    }

}

進入黑名單類:

public class BlackVoteState implements VoteState {

    @Override
    public void vote(String user, String voteItem, VoteManager voteManager) {
        // 記錄進入黑名單,禁止登錄系統
        System.out.println(user + ",你已進入黑名單,將禁止登錄和使用本系統");
    }

}

投票管理類:

public class VoteManager {
    // 持有具體狀態對象
    private VoteState state = null;
    // 記錄用戶投票的結果,數據記錄爲Map<用戶名稱,投票的選項>
    private Map<String, String> mapVote = new HashMap<String, String>();
    // 記錄用戶投票次數,數據記錄爲Map<用戶名稱,投票的次數>
    private Map<String, Integer> mapVoteCount = new HashMap<String, Integer>();

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

    /**
     * 投票邏輯處理方法
     * 
     * @param user  投票人
     * 
     * @param voteItem   投票的選項
     */
    public void vote(String user, String voteItem) {
        // 1.爲該用戶增加投票次數
        // 從記錄中取出該用戶已有的投票次數
        Integer oldVoteCount = mapVoteCount.get(user);
        if (oldVoteCount == null) {
            // 該用戶第一次投票情況,先實例化爲0,而後再添加
            oldVoteCount = 0;
        }
        oldVoteCount += 1;
        mapVoteCount.put(user, oldVoteCount);
        // 2.判斷該用戶的投票類型,就相當於判斷對應的狀態
        // 到底是正常投票、重複投票、惡意投票還是上黑名單的狀態

        if (oldVoteCount == 1) {
            state = new NormalVoteState();
        } else if (oldVoteCount > 1 && oldVoteCount < 5) {
            state = new RepeatVoteState();
        } else if (oldVoteCount >= 5 && oldVoteCount < 8) {
            state = new SpiteVoteState();
        } else if (oldVoteCount > 8) {
            state = new BlackVoteState();
        }

        // 然後轉調狀態對象來進行相應的操作
        state.vote(user, voteItem, this);
    }
}

客戶端測試類:

public class Client {

    public static void main(String[] args) {

        VoteManager vm = new VoteManager();
        for (int i = 0; i < 11; i++) {
            // 張三,投票B選項
            vm.vote("張三", "B");
        }
    }

}

運行結果:

這裏寫圖片描述

狀態模式的總結
理解狀態模式的狀態和行爲,狀態就是指對象中屬性的值,而行爲是指對象中的功能、或說對象中的方法。

狀態模式的目的就是分離狀態的行爲,通過維護狀態的變化來調用不同狀態對應的不同功能。即是說,狀態和行爲是相關聯的,關係大概是:狀態決定行爲

狀態模式中的狀態是運行時期才被改變的,所以行爲也會在運行期隨着狀態的改變而改變。

狀態模式的優點:

  • 狀態模式將具體的狀態和與之相關的行爲進行局部化,而後將不同的具體狀態分割開來,易於管理和維護。
  • 正是將不同的具體狀態分割開來,以減少不同狀態之間的耦合。
  • 所有特定的狀態和行爲都分別封裝在一個具體的狀態中,這種情況下,很容易增加新的狀態、以及實現狀態的裝換。

缺點:

  • 當業務邏輯慢慢的擴大之後,會導致過多的具體狀態類。並且環境狀態角色會變得臃腫,變得難以管理和維護。
發佈了129 篇原創文章 · 獲贊 146 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章