摘要:重構舊代碼,取而代之方式是將動作委托給狀態(tài)類。注這個模式是將狀態(tài)封裝成為獨立地類,并將動作委托給代表當前狀態(tài)的對象。
通過改變對象內部狀態(tài)幫助對象控制行為
以一個簡單例子說明,假設我們要模擬制造一臺糖果機器,對方給你的機器流程圖如下
ok,我們現(xiàn)在簡單分析這張狀態(tài)圖,可將狀態(tài)提取出來:有硬幣,無硬幣,售出糖果,糖果售空四個狀態(tài),行為動作提取出來:投入一個硬幣,退回一個硬幣,轉動曲柄,發(fā)放糖果四個行為,當然還有一些特殊情況,具體情況參考代碼
代碼如下(參考)
GumballMachine
public class GumballMachine { /** * 狀態(tài)模式 糖果機的狀態(tài)都用一個不同整數(shù)表示 * */ final static int SOLD_OUT = 0;// 糖果售空 final static int NO_QUARTER = 1;// 沒有投25分錢 final static int HAS_QUARTER = 2;// 投了25分錢 final static int SOLD = 3;// 糖果售出 // 當前狀態(tài) int state = SOLD_OUT; // 用來追蹤糖果數(shù)量 int count = 0; public GumballMachine(int count) { this.count = count; if (count > 0) { // 如果有糖果,機器變?yōu)闆]有投25分錢狀態(tài)(待購買狀態(tài)) state = NO_QUARTER; } } // 投入25分錢方法 public void insertQuarter() { if (state == HAS_QUARTER) { System.out.println("已經(jīng)投過幣了,不能再投了"); } else if (state == NO_QUARTER) { state = HAS_QUARTER; System.out.println("請投入一枚硬幣"); } else if (state == SOLD_OUT) { System.out.println("不能再投幣了,機器已經(jīng)售空了"); } else if (state == SOLD) { System.out.println("請稍等,正在為你出糖果"); } } // 退出25分錢方法 public void ejectQuarter() { // 1當客戶要退錢是 if (state == HAS_QUARTER) { System.out.println("零錢退回"); state = NO_QUARTER;// 進入沒投幣狀態(tài) } else if (state == NO_QUARTER) { System.out.println("你沒有投入硬幣"); } else if (state == SOLD) { System.out.println("對不起,你已經(jīng)轉動了曲軸,無法退幣了"); } else if (state == SOLD_OUT) { System.out.println("糖果售空,無法退幣"); } } // 轉動曲軸方法(顧客) public void turnCrank() { if (state == HAS_QUARTER) { System.out.println("請轉動曲軸"); state = SOLD;// 狀態(tài)變?yōu)槭鄢鰻顟B(tài) dispense();// 調用發(fā)放糖果方法 } else if (state == SOLD) { System.out.println("轉動兩次也不給你糖果"); } else if (state == NO_QUARTER) { System.out.println("請先投硬幣"); } else if (state == SOLD_OUT) { System.out.println("機器售空"); } } // 發(fā)放糖果方法 private void dispense() { if (state == SOLD) { System.out.println("一包糖果出來了"); count -= 1; // 當糖果數(shù)量0時候呢? if (count == 0) { System.out.println("哎呀,糖果售空了~~"); state = SOLD_OUT; } else { state = NO_QUARTER; } } else if (state == NO_QUARTER) { System.out.println("你需要投入硬幣"); } else if (state == SOLD_OUT) { System.out.println("沒有糖果了"); } else if (state == HAS_QUARTER) { System.out.println("沒有糖果了"); } } // 重寫toString()方法輸入糖果機信息 @Override public String toString() { StringBuffer result = new StringBuffer(); result.append("歡迎使用糖果機 "); result.append("糖果數(shù)量:" + count + " "); result.append("當前糖果機狀態(tài):" + state); return result.toString(); } }
TestMain
public class TestMain { public static void main(String[] args) { // 裝了5個糖 GumballMachine gumballMachine = new GumballMachine(5); System.out.println(gumballMachine + " ");// 打印糖果機信息 gumballMachine.insertQuarter();// 投入硬幣 gumballMachine.turnCrank();// 轉動曲柄 System.out.println(gumballMachine + " ");// 打印糖果機信息 gumballMachine.insertQuarter();// 投入硬幣 gumballMachine.ejectQuarter();// 要求退幣 gumballMachine.turnCrank();// 轉動曲柄,拿不到糖果 System.out.println(gumballMachine + " ");// 打印糖果機信息 gumballMachine.insertQuarter();// 投入硬幣 gumballMachine.turnCrank();// 轉動曲柄(拿到糖果) gumballMachine.insertQuarter();// 投入硬幣 gumballMachine.turnCrank();// 轉動曲柄(拿到糖果) gumballMachine.ejectQuarter();// 要求機器退錢 System.out.println(gumballMachine + " ");// 打印糖果機信息 gumballMachine.insertQuarter();// 投入硬幣 gumballMachine.insertQuarter();// 投入硬幣 gumballMachine.turnCrank();// 轉動曲柄(拿到糖果) // 下面開始壓力測試 //gumballMachine.insertQuarter();// 投入硬幣 //gumballMachine.turnCrank();// 轉動曲軸 //gumballMachine.insertQuarter();// 投入硬幣 //gumballMachine.turnCrank();// 轉動曲軸 System.out.println(gumballMachine + " ");// 打印糖果機信息 } }
效果圖(無壓力測試)
效果圖(壓力測試)
效果可以快速實現(xiàn),但這樣的代碼顯然還有不足之處,假設有個新的需求:增加一個幸運用戶,就是購買者有10%的概率可以一次買到兩顆糖果,這又要如何實現(xiàn)呢?先看流程圖
按照之前的1.0代碼加新中獎者功能,顯然不合適,要在每個方法里寫入,顯然太麻煩,這里就需要重構代碼了。我們可以試著行為封裝起來,也可以把糖果機器具體一下.我們要做的是如下:
1、首先定義一個State接口,在這個接口內,糖果機的每個動作都有一個對應的方法。
2、然后為機器中的每個狀態(tài)實現(xiàn)類。這些類將負責在對應的狀態(tài)下進行機器的行為。
3、重構舊代碼,取而代之方式是將動作委托給狀態(tài)類。
具體實現(xiàn)請看代碼
結構圖
State
package Interface; public interface State { /** * 將四種狀態(tài)抽象出來成基類 */ // 投入硬幣 public void insertQuarter(); // 退回硬幣 public void ejectQuarter(); // 轉動曲柄 public void turnCrank(); // 發(fā)放糖果 public void dispense(); }
GumballMachine
package Machine; public class GumballMachine { /** * 不在使用靜態(tài)整數(shù),都是用對象 * */ State soldOutState;// 糖果售空 State noQuarterState;// 沒有投幣 State hasQuarterState;// 投了幣 State soldState;// 糖果售出 State winnerState;// 中獎狀態(tài) // 當前狀態(tài),持有的是(糖果售空)對象 State state = soldOutState; int count = 0; // numberGumball構造器取得糖果的初始數(shù)目后,并把它存放在一個實例變量中 public GumballMachine(int numberGumballs) { soldOutState = new SoldOutState(this); noQuarterState = new NoQuarterState(this); hasQuarterState = new HasQuarterState(this); soldState = new SoldState(this); winnerState = new WinnerState(this); this.count = numberGumballs; if (numberGumballs > 0) { state = noQuarterState; } } /** * 機器的各個操作不在這里具體實現(xiàn)了 而是丟給接口,再讓具體實現(xiàn)類去實現(xiàn)接口 */ // 添加硬幣 public void insertQuarter() { state.insertQuarter(); } // 退出硬幣 public void ejectQuarter() { state.ejectQuarter(); } // 使用曲柄 public void turnCrank() { state.turnCrank(); state.dispense(); } // 變化狀態(tài) public void setState(State state) { this.state = state; } // 糖果出貨 public void releaseBall() { System.out.println("一包糖果出來了"); if (count != 0) { count = count - 1; } } public int getCount() { return count; } public State getState() { return state; } public State getSoldOutState() { return soldOutState; } public State getNoQuarterState() { return noQuarterState; } public State getHasQuarterState() { return hasQuarterState; } public State getSoldState() { return soldState; } public State getWinnerState() { return winnerState; } public String toString() { StringBuffer result = new StringBuffer(); result.append("歡迎使用糖果機 "); result.append("糖果數(shù)量:" + count + " "); result.append("當前糖果機狀態(tài): " + state + " "); return result.toString(); } }
各個行為類實現(xiàn)
HasQuarterState
package State_Implements; public class HasQuarterState implements State { /** * 投幣的實現(xiàn)類 * * @param gumballMachine */ // 增加一個隨機數(shù)產(chǎn)生器,10%機會 Random randomWinner = new Random(System.currentTimeMillis()); GumballMachine gumballMachine; public HasQuarterState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } public void insertQuarter() { System.out.println("你已經(jīng)投過幣了,不能再投了"); } public void ejectQuarter() { System.out.println("硬幣退出"); // 退出硬幣后,狀態(tài)變?yōu)闆]有硬幣(待購買)狀態(tài) gumballMachine.setState(gumballMachine.getNoQuarterState()); } public void turnCrank() { System.out.println("轉動....."); int winner = randomWinner.nextInt(10);// 產(chǎn)生0-9隨機數(shù),當是0并且還有糖果的時候中獎了 if ((winner == 0) && (gumballMachine.getCount() > 1)) { gumballMachine.setState(gumballMachine.getWinnerState()); } else { gumballMachine.setState(gumballMachine.getSoldState()); } } public void dispense() { System.out.println("沒有糖果出來"); } public String toString() { return "等待使用曲柄"; } }
NoQuarterState
package State_Implements; /** * 沒有投幣狀態(tài)實現(xiàn), * 都通通要實現(xiàn)狀態(tài)基類 * @author Joy * */ public class NoQuarterState implements State { GumballMachine gumballMachine; public NoQuarterState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } public void insertQuarter() { System.out.println("你投了一個硬幣"); gumballMachine.setState(gumballMachine.getHasQuarterState()); } public void ejectQuarter() { System.out.println("你沒有投幣,無法退幣~~"); } public void turnCrank() { System.out.println("你沒有投幣,無法繼續(xù)~~"); } public void dispense() { System.out.println("你需要投幣才能買糖果"); } public String toString() { return "正在運營"; } }
SoldOutState
package State_Implements; /** * 售空狀態(tài) * * @author Joy * */ public class SoldOutState implements State { GumballMachine gumballMachine; public SoldOutState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } public void insertQuarter() { System.out.println("抱歉,你不能在投幣了,糖果售空了"); } public void ejectQuarter() { System.out.println("抱歉,糖果售空,無法退幣"); } public void turnCrank() { System.out.println("抱歉,轉動曲柄無效,糖果售空了"); } public void dispense() { System.out.println("糖果售空了"); } public String toString() { return "糖果售空"; } }
SoldState
package State_Implements; /** * 賣出糖果狀態(tài) * * @author Joy * */ public class SoldState implements State { GumballMachine gumballMachine; public SoldState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } public void insertQuarter() { System.out.println("請稍等,糖果正出貨"); } public void ejectQuarter() { System.out.println("抱歉,你已使用曲柄,無法退幣"); } public void turnCrank() { System.out.println("曲柄不可重復使用"); } public void dispense() { // 調用糖果出貨方法 gumballMachine.releaseBall(); if (gumballMachine.getCount() > 0) { gumballMachine.setState(gumballMachine.getNoQuarterState()); } else { System.out.println("哎呀,糖果售空了"); gumballMachine.setState(gumballMachine.getSoldOutState()); } } public String toString() { return "一個糖果已出貨"; } }
WinnerState
package State_Implements; public class WinnerState implements State { GumballMachine gumballMachine; public WinnerState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } public void insertQuarter() { System.out.println("不能投幣"); } public void ejectQuarter() { System.out.println("不能投幣"); } public void turnCrank() { System.out.println("不能使用曲柄"); } public void dispense() { System.out.println("恭喜中獎了,你得到兩個糖果~~"); gumballMachine.releaseBall(); // 此時糖果機器里只有一顆時候,那么第二顆就出不來,狀態(tài)變?yōu)槭劭諣顟B(tài) if (gumballMachine.getCount() == 0) { gumballMachine.setState(gumballMachine.getSoldOutState()); } else { gumballMachine.releaseBall(); if (gumballMachine.getCount() > 0) { gumballMachine.setState(gumballMachine.getNoQuarterState()); } else { System.out.println("哎呀,糖果售空了"); gumballMachine.setState(gumballMachine.getSoldOutState()); } } } public String toString() { return "你是中獎者,得到兩個糖果"; } }
GumballMachineTestDrive 測試類
package TestMain; import Machine.GumballMachine; public class GumballMachineTestDrive { public static void main(String[] args) { // 一開始5顆糖 GumballMachine gumballMachine = new GumballMachine(5); System.out.println(gumballMachine + " "); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine + " ");// 輸出狀態(tài) gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine + " ");// 輸出狀態(tài) } }
效果圖
狀態(tài)模式定義:允許對象在內部狀態(tài)改變時改變它的行為,對象看起來好像修改了它的類。
注:這個模式是將狀態(tài)封裝成為獨立地類,并將動作委托給代表當前狀態(tài)的對象。
狀態(tài)模式類圖如下,后來本人在回顧時發(fā)現(xiàn)狀態(tài)模式和策略模式類圖很相似,有興趣朋友可以將兩者去比較不同
感謝你看到這里,狀態(tài)模式到這里就結束了,本人文筆隨便,若有不足或錯誤之處望給予指點,90度彎腰~~~很快我會發(fā)布下一個設計模式的內容,生命不息,編程不止!
參考書籍:《Head First 設計模式》
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/67916.html
摘要:工作單元用于維護受業(yè)務事務影響的對象列表,并協(xié)調變化的寫入和并發(fā)問題的解決。工作單元控制器工作單元控制所有數(shù)據(jù)庫的讀操作,一旦對象被讀取,將將它注冊為干凈的對象。 1. 工作單元 用于維護受業(yè)務事務影響的對象列表,并協(xié)調變化的寫入和并發(fā)問題的解決。如下: showImg(https://segmentfault.com/img/remote/1460000018095222?w=162...
摘要:探究系統(tǒng)登錄驗證碼的實現(xiàn)后端掘金驗證碼生成類手把手教程后端博客系統(tǒng)第一章掘金轉眼間時間就從月份到現(xiàn)在的十一月份了。提供了與標準不同的工作方式我的后端書架后端掘金我的后端書架月前本書架主要針對后端開發(fā)與架構。 Spring Boot干貨系列總綱 | 掘金技術征文 - 掘金原本地址:Spring Boot干貨系列總綱博客地址:http://tengj.top/ 前言 博主16年認識Spin...
摘要:歡迎進入基礎課程博客地址本系列文章將主要針對一些基礎知識點進行講解,為平時歸納所總結,不管是剛接觸開發(fā)菜鳥還是業(yè)界資深人士,都希望對廣大同行帶來一些幫助。語法是,或者更一般的,要求構造器方法是沒有參數(shù)靜態(tài)方法引用。 歡迎進入JAVA基礎課程 博客地址:https://blog.csdn.net/houjiyu...本系列文章將主要針對JAVA一些基礎知識點進行講解,為平時歸納所總結,...
摘要:歡迎進入基礎課程博客地址本系列文章將主要針對一些基礎知識點進行講解,為平時歸納所總結,不管是剛接觸開發(fā)菜鳥還是業(yè)界資深人士,都希望對廣大同行帶來一些幫助。語法是,或者更一般的,要求構造器方法是沒有參數(shù)靜態(tài)方法引用。 歡迎進入JAVA基礎課程 博客地址:https://blog.csdn.net/houjiyu...本系列文章將主要針對JAVA一些基礎知識點進行講解,為平時歸納所總結,...
摘要:我的是忙碌的一年,從年初備戰(zhàn)實習春招,年三十都在死磕源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實習。因為我心理很清楚,我的目標是阿里。所以在收到阿里之后的那晚,我重新規(guī)劃了接下來的學習計劃,將我的短期目標更新成拿下阿里轉正。 我的2017是忙碌的一年,從年初備戰(zhàn)實習春招,年三十都在死磕JDK源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實習offer。然后五月懷著忐忑的心情開始了螞蟻金...
閱讀 3116·2023-04-25 16:50
閱讀 921·2021-11-25 09:43
閱讀 3533·2021-09-26 10:11
閱讀 2529·2019-08-26 13:28
閱讀 2542·2019-08-26 13:23
閱讀 2433·2019-08-26 11:53
閱讀 3577·2019-08-23 18:19
閱讀 3000·2019-08-23 16:27