設計模式(十一)—— 狀態模式
阿新 • • 發佈:2018-12-12
一、含義
允許物件在內部狀態改變時改變它的行為,物件看起來好像修改了它的類。也就是說當一個物件有許多狀態的時候,我們可以把每個物件抽離出來作為一個具體的類。
二、要點
1.狀態模式允許一個物件基於內部狀態而擁有不同的行為。
2.通過將每個狀態封裝進一個類,我們把以後需要做的任何改變區域性化了。
3.使用狀態模式通常會導致設計中類的數目大量增加。
三、實戰分析外觀模式
首先看一下狀態模式的類圖:
例如現在有需要自動售貨機,現在假設一個自動售貨機只賣一種飲料,只需5塊錢,對於售貨機有四種狀態就是,沒有投錢,投入5塊錢,出售飲料,飲料售空。現在讓我們用程式碼,來編寫自動售貨機這個收穫流程:
public class DrinksMachine { //對應自動售貨機的四種狀態 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 DrinksMachine(int count) { this.count = count; if (count > 0) { state = NO_QUARTER; } } //當我們投入5塊錢時,會執行這個方法 public void insertQuarter() { if (state == HAS_QUARTER) { System.out.println("已投錢,請勿重複投幣"); } else if (state == NO_QUARTER) { state = HAS_QUARTER; System.out.println("投幣成功"); } else if (state == SOLD_OUT) { System.out.println("請莫投幣,飲料已售空"); } else if (state == SOLD) { System.out.println("請等待,我們正在出貨"); } } //當我們嘗試退款時,執行的方法 public void ejectQuarter() { if (state == HAS_QUARTER) { System.out.println("正在退錢,請稍等"); state = NO_QUARTER; } else if (state == NO_QUARTER) { System.out.println("未投錢,無法退款"); } else if (state == SOLD) { System.out.println("無法退錢,正在給你出貨"); } else if (state == SOLD_OUT) { System.out.println("飲料已售空,未首款"); } } //當我們按下按鈕準備買飲料時,會呼叫此方法 public void turnCrank() { if (state == SOLD) { System.out.println("正在出貨請稍等,不要重複按鈕"); } else if (state == NO_QUARTER) { System.out.println("還未投錢,請先投錢"); } else if (state == SOLD_OUT) { System.out.println("不好意思,已售空"); } else if (state == HAS_QUARTER) { System.out.println("出貨"); state = SOLD; sale(); } } //出售飲料方法 public void sale() { if (state == SOLD) { System.out.println("正在出貨"); count --; 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("機器故障"); } } }
從上述程式碼可以看出,我們每個方法都要對售貨機的每個狀態都要進行判斷,這顯得程式碼複用性不高,而且如果我們可以加入新的狀態時,我們需要修改大量的程式碼,這違反了類應該對擴充套件開放,對修改關閉的原則。這個我們的狀態模式就可以大顯神威了,利用狀態模式,重寫這個自動收貨機,結合上面的類圖,我們需要把每個狀態單獨封裝成一個類,並實現一個共同的介面:
//狀態的通用介面 public interface state { void insertQuarter(); void ejectQuarter(); void turnCrank(); void sale(); }
在編寫對應的具體的狀態類
//其中的一個例子,其他的三個狀態大體相同
public class NoQuarterState implements State {
DrinksMachine drinksMachine;
public NoQuarterState(DrinksMachine drinksMachine) {
this.drinksMachine = drinksMachine;
}
//當有人投錢時
@Override
public void insertQuarter() {
System.out.println("你已投錢");
drinksMachine.setState(drinksMachine.getHasQuarterState());
}
@Override
public void ejectQuarter() {
System.out.println("你未投錢");
}
@Override
public void turnCrank() {
System.out.println("沒有錢,無法出售");
}
@Override
public void sale() {
System.out.println("沒有錢,無法出售");
}
}
最後我們在將自己售貨機的程式碼給修改:
public class DrinksMachine {
//對應著自己狀態
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State state = soldOutState;
int count = 0;
public DrinksMachine(int number) {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
this.count = number;
if (number > 0) {
state = noQuarterState;
}
}
public void insertQuarter() {
state.insertQuarter();
}
public void ejectQuarter() {
state.ejectQuarter();
}
public void turnCrank() {
state.turnCrank();
}
public void sale() {
state.sale();
}
//每個狀態的get/set方法
}
這看著上就簡潔的多,並且在以後有新的狀態時,只需新增新的狀態類就可以。