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