設計模式學習專欄十一--------狀態(tài)模式
名稱: 狀態(tài)模式 (State)
價值觀念: 通過改變對象內部的狀態(tài)來幫助對象控制自己的行為
場景
設計一個萬能糖果機 , 我們希望設計盡可能有彈性 , 而且將來我們可能要為它增加更多的行為~
剛開始的設計方式
public class GumballMachine {
final static int SOLD_OUT = 0;
final static int NO_QUARTER = 1;
final static int HAS_QUARTER = 2;
final static int SOLD = 3;
int state = SOLD_OUT;
int count = 0;
public GumballMachine(int count) {
this.count = count;
if (count > 0) {
state = NO_QUARTER;
}
}
public void insertQuarter() {
if (state == HAS_QUARTER) {
System.out.println("You can't insert another quarter");
} else if (state == NO_QUARTER) {
state = HAS_QUARTER;
System.out.println("You inserted a quarter");
} else if (state == SOLD_OUT) {
System.out.println("You can't insert a quarter, the machine is sold out");
} else if (state == SOLD) {
System.out.println("Please wait, we're already giving you a gumball");
}
}
public void ejectQuarter() {
if (state == HAS_QUARTER) {
System.out.println("Quarter returned");
state = NO_QUARTER;
} else if (state == NO_QUARTER) {
System.out.println("You haven't inserted a quarter");
} else if (state == SOLD) {
System.out.println("Sorry, you already turned the crank");
} else if (state == SOLD_OUT) {
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
}
......
}
我們的第一版設計完成了 , 發(fā)現(xiàn)每個動作下都需要判斷當前的狀態(tài),然后做出相應的動作.
新功能引入 , 我們加入了一個新的游戲狀態(tài) , 當曲柄被轉動時,有10%的幾率掉下來的是兩顆糖果(贏家狀態(tài))
此時我們會發(fā)現(xiàn)第一版設計中. 每個動作下 都需要新增條件判斷 , 違反了對修改關閉的原則. 程序出錯概率大大提升.
如何解決
分析程序擴展時的 可變部分與不變部分
insertCoin | returnCoin | trunCrank | dispense | |
---|---|---|---|---|
OnReadyState | - | - | - | - |
HasCoin | - | - | - | - |
SoldState | - | - | - | - |
SoldOutState | - | - | - | - |
WinnerState | *** | *** | *** | *** |
不變部分: 從橫向來看。 用戶能執(zhí)行的操作都是一樣的。 (插入硬幣旧乞,按下退幣按鈕慧库,拉下把手)
變化部分: 從橫向看膏潮。如果糖果工廠新增的狀態(tài)裸弦, 對于用戶每一種動作劲适,糖果機的響應都是不同的楷掉。都要做出對應的修改
將可變部分抽取出來: 每一種狀態(tài)都會執(zhí)行4種操作,糖果機具體的操作與當前狀態(tài)有關霞势。 因此將狀態(tài)與該狀態(tài)下的對應的動作行為抽取
出來形成接口烹植。讓每一個狀態(tài)都實現(xiàn)該接口。
狀態(tài)模式總覽
定義:允許
對象在內部狀態(tài)在改變時改變它的行為
,對象看起來好像改變了它的類
(將狀態(tài)與該狀態(tài)下的行為封裝成獨立的類,并將動作委托
到代表當前狀態(tài)的對象)
-
模式的理解
-
類圖
-
角色
- 上下文Context
- 封裝狀態(tài)及該狀態(tài)下行為的 State
- 具體的狀態(tài)實習那類ConcreteState
-
細節(jié)
- 狀態(tài)模式允許一個對象基于內部狀態(tài)而擁有不同的行為
- 通過把每個狀態(tài)封裝進一個類, 我們把以后需要做的任何改變都局部化了 (改變這個狀態(tài)下的行為)
- 狀態(tài)模式與策略模式有相同的類圖 ,但是它們的意圖不同
- 策略模式通常會用行為或算法來配置Context類
- 狀態(tài)模式允許Context隨著狀態(tài)的改變而改變行為
- 狀態(tài)轉換可以由State類(某個行為后改變狀態(tài))或者Context(外部設置狀態(tài)setState)類控制.
- 使用狀態(tài)模式通常會導致設計中的類的數(shù)據(jù)大量增量(狀態(tài)類)
-
核心代碼部分
-
上下文
public class GumballMachine { //上下文持有不同的狀態(tài)引用 State soldOutState; State noQuarterState; State hasQuarterState; State soldState; State winnerState; State state = soldOutState; int count = 0; 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; } } //上下文提供改變狀態(tài)的接口 void setState(State state) { this.state = state; } public void insertQuarter() { state.insertQuarter(); //將行為委托給狀態(tài)對象來處理 } public void ejectQuarter() { state.ejectQuarter(); } public void turnCrank() { state.turnCrank(); state.dispense(); } void releaseBall() { System.out.println("A gumball comes rolling out the slot..."); if (count != 0) { count = count - 1; } } int getCount() { return count; } void refill(int count) { this.count += count; System.out.println("The gumball machine was just refilled; it's new count is: " + this.count); state.refill(); } getter and setter... }
-
狀態(tài)接口
public interface State { public void insertQuarter(); public void ejectQuarter(); public void turnCrank(); public void dispense(); public void refill(); }
- 具體的狀態(tài)
public class WinnerState implements State {
GumballMachine gumballMachine;
public WinnerState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("Please wait, we're already giving you a Gumball");
}
public void ejectQuarter() {
System.out.println("Please wait, we're already giving you a Gumball");
}
public void turnCrank() {
System.out.println("Turning again doesn't get you another gumball!");
}
public void dispense() {
gumballMachine.releaseBall();
if (gumballMachine.getCount() == 0) {
//通過上下文改變狀態(tài)
gumballMachine.setState(gumballMachine.getSoldOutState());
} else {
gumballMachine.releaseBall();
System.out.println("YOU'RE A WINNER! You got two gumballs for your quarter");
if (gumballMachine.getCount() > 0) {
gumballMachine.setState(gumballMachine.getNoQuarterState());
} else {
System.out.println("Oops, out of gumballs!");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
}
public void refill() { }
public String toString() {
return "despensing two gumballs for your quarter, because YOU'RE A WINNER!";
}
}
-
主程序
public class GumballMachineTestDrive { public static void main(String[] args) { GumballMachine gumballMachine = new GumballMachine(10); System.out.println(gumballMachine); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine); } }
-
輸出結果
Mighty Gumball, Inc. Java-enabled Standing Gumball Model #2004 Inventory: 10 gumballs Machine is waiting for quarter You inserted a quarter You turned... A gumball comes rolling out the slot... You inserted a quarter You turned... A gumball comes rolling out the slot... Mighty Gumball, Inc. Java-enabled Standing Gumball Model #2004 Inventory: 8 gumballs Machine is waiting for quarter
參考
? 書籍: HeadFirst設計模式
? 代碼參考地址: 我就是那個地址