Head First設計模式讀書筆記九 第十章 狀態模式
阿新 • • 發佈:2018-12-11
狀態模式例項
用Java設計糖果機吧 大致流程: 上圖中,有四種狀態:沒有硬幣,有硬幣,準備售出狀態以及糖果售罄狀態。而控制糖果機狀態轉換的則是各種動作(Action),這些動作分別是投入硬幣,超時判斷,轉動曲柄,判斷糖果決定是否售出。 GumballMachine類
public class GumballMachine { //define 4 states private final int SOLD_OUT=0; private final int NO_COIN=1; private final int HAS_COIN=2; private final int START_TO_SOLD=3; int stateNow = SOLD_OUT; int count = 0; public GumballMachine(int count) { if (count > 0){ stateNow = NO_COIN; } this.count = count; } //define 4 actions public void insertCoin(){ switch (stateNow) { case SOLD_OUT: System.out.println("機器售罄,請勿投幣"+getState()); break; case NO_COIN: //正常case stateNow = HAS_COIN; System.out.println("你投入了硬幣"+getState()); break; case HAS_COIN: System.out.println("你已經投過幣了,無法投幣"+getState()); break; case START_TO_SOLD: System.out.println("請稍後投幣,機器處理中"+getState()); break; default: System.out.println("unknown state"); break; } } public void ejectCoin(){ switch (stateNow) { case SOLD_OUT: System.out.println("你沒有投幣"+getState()); break; case NO_COIN: System.out.println("你沒有投幣"+getState()); break; case HAS_COIN: //正常case stateNow = NO_COIN; System.out.println("等待超時,退幣。或者你按下了退幣健"+getState()); break; case START_TO_SOLD: System.out.println("你已經旋轉曲柄,機器已經準備售出,無法退幣"+getState()); break; default: System.out.println("unknown state"); break; } } public void turnCrank(){ switch (stateNow) { case SOLD_OUT: System.out.println("糖果售罄,請勿操作"); break; case NO_COIN: System.out.println("請先投幣"); break; case HAS_COIN: //正常case stateNow = START_TO_SOLD; System.out.println("你轉動了曲柄"+getState()); break; case START_TO_SOLD: System.out.println("機器處理中,請勿操作"); break; default: System.out.println("unknown state"); break; } } //發放糖果 public void dispense(){ switch (stateNow) { case SOLD_OUT: System.out.println("請正確操作本機"); break; case NO_COIN: System.out.println("請先投幣"); break; case HAS_COIN: System.out.println("先轉動曲柄"); break; case START_TO_SOLD: //正常case count --; if(count > 0){ stateNow = NO_COIN; System.out.println("售出糖果"+getState()); }else{ stateNow = SOLD_OUT; System.out.println("售出糖果"+getState()); } break; default: System.out.println("unknown state"); break; } } public String getState(){ switch (stateNow) { case SOLD_OUT: return (" 目前狀態:糖果售罄狀態"); case NO_COIN: return (" 目前狀態:沒有硬幣狀態"); case HAS_COIN: return (" 目前狀態:有硬幣狀態"); case START_TO_SOLD: return (" 目前狀態:準備售出狀態"); default: return " 目前狀態:未知狀態"; } } }
測試類
public class Test { public static void main(String[] args) { GumballMachine gumballMachine = new GumballMachine(3); gumballMachine.insertCoin(); gumballMachine.turnCrank(); gumballMachine.dispense(); System.out.println("================"); gumballMachine.dispense(); gumballMachine.turnCrank(); gumballMachine.insertCoin(); System.out.println("================"); gumballMachine.insertCoin(); gumballMachine.turnCrank(); gumballMachine.dispense(); System.out.println("================"); gumballMachine.insertCoin(); gumballMachine.turnCrank(); gumballMachine.dispense(); System.out.println("================"); gumballMachine.insertCoin(); gumballMachine.turnCrank(); gumballMachine.dispense(); } }
測試結果
你投入了硬幣 目前狀態:有硬幣狀態 你轉動了曲柄 目前狀態:準備售出狀態 售出糖果 目前狀態:沒有硬幣狀態 ================ 請先投幣 請先投幣 你投入了硬幣 目前狀態:有硬幣狀態 ================ 你已經投過幣了,無法投幣 目前狀態:有硬幣狀態 你轉動了曲柄 目前狀態:準備售出狀態 售出糖果 目前狀態:沒有硬幣狀態 ================ 你投入了硬幣 目前狀態:有硬幣狀態 你轉動了曲柄 目前狀態:準備售出狀態 售出糖果 目前狀態:糖果售罄狀態 ================ 機器售罄,請勿投幣 目前狀態:糖果售罄狀態 糖果售罄,請勿操作 請正確操作本機
新加功能,中獎機制
客戶希望給機器新增中獎機制,即使用者每次買糖果,有10%的機率獲得2個糖果. 我們可以看到GumballMachine這個類太過複雜,狀態轉換全都包含其中,因此我們需要做一些改變了。 1.定義狀態介面
public interface State {
//define 4 actions
public void insertCoin();
public void ejectCoin();
public void turnCrank();
public void dispense();
public String getState();
}
2.重新實現GumballMachine類
public class GumballMachine {
// define 4 states
State soldOutState;
State hasCoinState;
State noCoinState;
State soldState;
State winnerState;
int count = 0;
State stateNow = soldOutState;
public GumballMachine(int count) {
soldOutState = new SoldOutState(this);
hasCoinState = new HasCoinState(this);
noCoinState = new NoCoinState(this);
soldState = new SoldState(this);
winnerState = new WinnerState(this);
if (count > 0) {
stateNow = noCoinState;
}
this.count = count;
}
public void insertCoin() {
stateNow.insertCoin();
}
public void ejectCoin() {
stateNow.ejectCoin();
}
public void turnTrunk() {
stateNow.turnCrank();
stateNow.dispense();
}
void setState(State state) {
stateNow = state;
}
void releaseBall() {
if (count > 0) {
count--;
System.out.println("A gumball rolling out the slot...");
} else {
System.out.println("release ball failed");
}
}
public State getSoldOutState() {
return soldOutState;
}
public State getHasCoinState() {
return hasCoinState;
}
public State getNoCoinState() {
return noCoinState;
}
public State getSoldState() {
return soldState;
}
public State getWinnerState() {
return winnerState;
}
public int getCount() {
return count;
}
public State getStateNow() {
return stateNow;
}
}
3.實現狀態類: 將原先
private final int SOLD_OUT=0;
private final int NO_COIN=1;
private final int HAS_COIN=2;
private final int START_TO_SOLD=3;
的設計改到類中;這樣,原先我們是操作時需要判斷當前狀態。而現在我們是狀態被固定,需要看能進行哪些操作(狀態控制動作)。而且,我們不需要主動控制狀態,狀態由程式碼自動控制。
public class HasCoinState implements State {
Random randomWinner = new Random(System.currentTimeMillis());
GumballMachine gumballMachine;
public HasCoinState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertCoin() {
System.out.println("你已經投過幣了");
}
public void ejectCoin() {
System.out.println("你請求了退幣");
gumballMachine.setState(gumballMachine.getNoCoinState());
}
public void turnCrank() {
System.out.println("你轉動了曲柄");
int winner = randomWinner.nextInt(10);
if(winner ==0 && gumballMachine.getCount()>1){
gumballMachine.setState(gumballMachine.getWinnerState());
}else{
gumballMachine.setState(gumballMachine.getSoldState());
}
}
public void dispense() {
System.out.println("請先轉動曲柄");
}
public String getState() {
return this.getClass().getName();
}
}
public class NoCoinState implements State {
GumballMachine gumballMachine;
public NoCoinState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertCoin() {
System.out.println("你投幣了");
gumballMachine.setState(gumballMachine.getHasCoinState());
}
public void ejectCoin() {
System.out.println("你還沒有投幣");
}
public void turnCrank() {
System.out.println("你還沒有投幣,轉杆無效");
}
public void dispense() {
System.out.println("還沒有投幣,不能發放糖果");
}
public String getState() {
return this.getClass().getName();
}
}
public class SoldOutState implements State {
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertCoin() {
System.out.println("糖果售罄,請勿操作");
}
public void ejectCoin() {
System.out.println("糖果售罄,請勿操作");
}
public void turnCrank() {
System.out.println("糖果售罄,請勿操作");
}
public void dispense() {
System.out.println("糖果售罄,請勿操作");
}
public String getState() {
return this.getClass().getName();
}
}
public class SoldState implements State {
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertCoin() {
System.out.println("機器處理中,請稍後");
}
public void ejectCoin() {
System.out.println("機器已經處理,無法操作");
}
public void turnCrank() {
System.out.println("你已經轉動過曲柄了");
}
public void dispense() {
gumballMachine.releaseBall();
if(gumballMachine.count>0){
gumballMachine.setState(gumballMachine.getNoCoinState());
System.out.println("發放糖果");
}else{
System.out.println("發放糖果完畢,糖果售罄");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
public String getState() {
return this.getClass().getName();
}
}
public class WinnerState implements State {
GumballMachine gumballMachine;
public WinnerState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertCoin() {
System.out.println("機器正在處理,請稍後投幣");
}
public void ejectCoin() {
System.out.println("機器正在處理,無法退幣");
}
public void turnCrank() {
System.out.println("機器正在處理,無法操作");
}
public void dispense() {
gumballMachine.releaseBall();
if(gumballMachine.getCount() == 0){
gumballMachine.setState(gumballMachine.getSoldOutState());
}else{
System.out.println("你獲得了獎勵");
gumballMachine.releaseBall();
if(gumballMachine.getCount() == 0){
gumballMachine.setState(gumballMachine.getSoldOutState());
}else{
gumballMachine.setState(gumballMachine.getNoCoinState());
}
}
}
public String getState() {
return null;
}
}
4.修改測試類
public class Test {
public static void main(String[] args) {
GumballMachine gumballMachine = new GumballMachine(5);
for(int i =0;i<5; i++){
gumballMachine.insertCoin();
gumballMachine.turnTrunk();
System.out.println(gumballMachine.getStateNow().getState()+"---------------");
}
}
}
5.測試結果:
你投幣了
你轉動了曲柄
A gumball rolling out the slot...
發放糖果
bean.NoCoinState---------------
你投幣了
你轉動了曲柄
A gumball rolling out the slot...
發放糖果
bean.NoCoinState---------------
你投幣了
你轉動了曲柄
A gumball rolling out the slot...
發放糖果
bean.NoCoinState---------------
你投幣了
你轉動了曲柄
A gumball rolling out the slot...
你獲得了獎勵
A gumball rolling out the slot...
bean.SoldOutState---------------
糖果售罄,請勿操作
糖果售罄,請勿操作
糖果售罄,請勿操作
bean.SoldOutState---------------
狀態模式的簡單分析
狀態模式存在幾個簡單部分: context state Context意為上下文,在這裡即是GumballMachine,而state在這裡就是各個state的實現類了。 可以看到GumballMachine中包含了各類狀態的引用例項,而GumballMachine也存在自己當前狀態的例項 GumballMachine類的操作看上去是交給了State實現類,但實際上,State中存在Context的引用,State實現類在操作時通過GumballMachine引用來控制GumballMachine狀態。即下圖: