《大話設計模式》——讀後感 (10)無盡加班何時休?——狀態模式
原文定義:
狀態模式:當一個對象的內在狀態改變時允許改變其行為,這個對象看起來像是改變其類【DP】
UML結構圖:
背景:
看到此模式,醞釀了好久才決定對狀態模式進行總結。光看原文定義,實在沒有獲取到什麽有用的價值。
第一眼看到狀態模式,感覺這不就是一個簡單工工廠模式嗎?但是仔細看看其他人的博客,發現狀態模式和簡單工廠模式還是有一定的區別的,最明顯的是Context類持有了State,這一點和簡單工廠區別很大。但是我發現狀態模式又和策略模式很像,或者說是太像了。同樣有抽象接口,具體實現類,Context上下文類, 以及Context裏面也含有一個State抽象,簡直一摸一樣啊。而且網上其他很多人的博客在解釋狀態模式的時候,用的分明就是策略模式的例子,真的很無解!
思量再三,對《大話設計模式》中對狀態模式進行反復的研究,並且上網找了很多的微博進行閱讀和理解,最終才有一點收獲,並且再次進行總結。
其實看了很多的微博以後,再次來理解定義的時候,我們還是能獲取一點有用的信息的:
狀態模式:當一個對象的內在狀態改變時允許改變其行為,這個對象看起來像是改變其類【DP】
1、"對象的內在狀態":其實這和我們在策略模式中理解的具體策略,在簡單工廠中的具體算法是類似的,稱呼不同而已。
2、“當一個對象的內在狀態改變時允許改變其行為”: 舉例說A對象內部持有的B改變了,此時允許B改變A的具體行為操作,即狀態控制行為。
適用性
在下面的兩種情況下均可使用State模式:
1) ? 一個對象的行為取決於它的狀態, 並且它必須在運行時刻根據狀態改變它的行為。
2) ? 代碼中包含大量與對象狀態有關的條件語句:一個操作中含有龐大的多分支的條件(if else(或switch case)語句,且這些分支依賴於該對象的狀態。這個狀態通常用一個或多個枚舉常量表示。通常 , 有多個操作包含這一相同的條件結構。 State模式將每一個條件分支放入一個獨立的類中。這使得你可以根據對象自身的情況將對象的狀態作為一個對象,這一對象可以不依賴於其他對象而獨立變化。
下面分享我在網上找到的一個關於投票的優秀案例,我對其進行改造,看起來更加的符合狀態模式定義:
考慮一個在線投票系統的應用,要實現控制同一個用戶只能投一票,如果一個用戶反復投票,而且投票次數超過5次,則判定為惡意刷票,要取消該用戶投票的資格,當然同時也要取消他所投的票;如果一個用戶的投票次數超過8次,將進入黑名單,禁止再登錄和使用系統。
要使用狀態模式實現,首先需要把投票過程的各種狀態定義出來,根據以上描述大致分為四種狀態:正常投票、反復投票、惡意刷票、進入黑名單。然後創建一個投票管理對象(相當於Context)。
投票的抽象接口:
package com.sjmx.state; public interface VoteState { /** * 處理狀態對應的行為 * * @param user * 投票人 * @param voteItem * 投票項 * @param voteManager * 投票上下文,用來在實現狀態對應的功能處理的時候, 可以回調上下文的數據 */ public void vote(String user, String voteItem, VoteManager voteManager,int count); }
具體狀態類——正常投票:
package com.sjmx.state; /* * * 具體狀態類——正常投票 */ public class NormalVoteState implements VoteState { @Override public void vote(String user, String voteItem, VoteManager voteManager,int count) { // 正常投票,記錄到投票記錄中 if(1 == count){ voteManager.getMapVote().put(user, voteItem); System.out.println("恭喜投票成功,投票內容為:" + voteItem); voteManager.state = new RepeatVoteState(); } } }
具體狀態類——重復投票:
package com.sjmx.state; /* * * 具體狀態類——重復投票 */ public class RepeatVoteState implements VoteState { @Override public void vote(String user, String voteItem, VoteManager voteManager,int count) { // 重復投票,暫時不做處理 if(count > 1 && count < 5){ System.out.println("請不要重復投票,投票內容為:" + voteItem); }else{ voteManager.state = new SpiteVoteState(); voteManager.voteByChange(user, voteItem, count); } } }
惡意投票:
package com.sjmx.state; public class SpiteVoteState implements VoteState { @Override public void vote(String user, String voteItem, VoteManager voteManager,int count) { // 惡意投票,取消用戶的投票資格,並取消投票記錄 if(count >= 5 && count < 8) { System.out.println("count:" + count); String str = voteManager.getMapVote().get(user); if (str != null) { voteManager.getMapVote().remove(user); } System.out.println("你有惡意刷屏行為,取消投票資格!-----投票內容為:" + voteItem); }else{ voteManager.state = new BlackVoteState(); voteManager.voteByChange(user, voteItem, count); } } }
記錄黑名單中,禁止登錄系統:
package com.sjmx.state; public class BlackVoteState implements VoteState { @Override public void vote(String user, String voteItem, VoteManager voteManager,int count) { // 記錄黑名單中,禁止登錄系統 System.out.println("進入黑名單,將禁止登錄和使用本系統,投票內容為:" + voteItem); } }
狀態管理類:
package com.sjmx.state; import java.util.HashMap; import java.util.Map; public class VoteManager { // 持有狀體處理對象 public VoteState state = null; // 記錄用戶投票的結果,Map<String,String>對應Map<用戶名稱,投票的選項> public Map<String, String> mapVote = new HashMap<String, String>(); // 記錄用戶投票次數,Map<String,Integer>對應Map<用戶名稱,投票的次數> public Map<String, Integer> mapVoteCount = new HashMap<String, Integer>(); public VoteManager(String user) { state = new NormalVoteState(); } /** * 獲取用戶投票結果的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) { oldVoteCount = 0; } oldVoteCount += 1; mapVoteCount.put(user, oldVoteCount); this.voteByChange(user, voteItem, oldVoteCount); } public void voteByChange(String user, String voteItem, int count){ // 然後轉調狀態對象來進行相應的操作 state.vote(user, voteItem, this,count); } }
客戶端:
package com.sjmx.state; public class Client { public static void main(String[] args) { VoteManager vm = new VoteManager("jack"); for(int i=1;i<11;i++){ vm.vote("u1","A"+i); } } }
運行結果:
恭喜投票成功,投票內容為:A1 請不要重復投票,投票內容為:A2 請不要重復投票,投票內容為:A3 請不要重復投票,投票內容為:A4 count:5 你有惡意刷屏行為,取消投票資格!-----投票內容為:A5 count:6 你有惡意刷屏行為,取消投票資格!-----投票內容為:A6 count:7 你有惡意刷屏行為,取消投票資格!-----投票內容為:A7 進入黑名單,將禁止登錄和使用本系統,投票內容為:A8 進入黑名單,將禁止登錄和使用本系統,投票內容為:A9 進入黑名單,將禁止登錄和使用本系統,投票內容為:A10
《大話設計模式》——讀後感 (10)無盡加班何時休?——狀態模式