正文
一、定義
狀態模式允許物件在內部狀態改變時改變它的行為,物件看起來好像修改了它的類。
要點:
- 狀態模式允許一個物件基於內部狀態而擁有不同的行為。
- 狀態模式將狀態封裝成為獨立的類,並將動作委託到代表當前狀態的物件。
- 通過將每個狀態封裝進一個類,我們把以後需要做的任何改變區域性化了。
二、實現步驟
1、建立狀態介面
/**
* 狀態介面
*/
public interface State {
/**
* 根據狀態進行處理的方法
*/
public void handle();
}
2、在持有狀態的類中,將請求委託給狀態類
/**
* 持有狀態的上下文類
*/
public class Context {
private State state;
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
/**
* 接收請求,並將請求委託給狀態類
*/
public void request() {
state.handle();
}
}
3、建立具體的狀態,並實現狀態介面
(1)具體狀態A
/**
* 具體狀態A
*/
public class ConcreteStateA implements State {
Context context;
public ConcreteStateA() {
context = new Context();
}
@Override
public void handle() {
// 實現該狀態下相應的行為
System.out.println("Context is in A state, and start to do something...");
context.setState(this);
}
}
(2)具體狀態B
/**
* 具體狀態B
*/
public class ConcreteStateB implements State {
Context context;
public ConcreteStateB() {
context = new Context();
}
@Override
public void handle() {
// 實現該狀態下相應的行為
System.out.println("Context is in B state, and start to do something...");
context.setState(this);
}
}
4、通過改變狀態,來改變上下文類的行為
public class Test {
public static void main(String[] args) {
// 上下文
Context context = new Context();
// 狀態
State stateA = new ConcreteStateA();
State stateB = new ConcreteStateB();
// 通過狀態改變行為
context.setState(stateA);
context.request();
context.setState(stateB);
context.request();
}
}
三、舉個例子
1、背景
萬能糖果公司打算使用 Java 來實現糖果機的控制器。他們希望設計能夠儘量有彈性而且好維護,因為將來可能要為糖果機增加更多的行為。
糖果機的工作流程如下:
2、實現
(1)建立狀態介面,並定義相應的糖果機行為
/**
* 狀態介面
*/
public interface State {
/**
* 投入25分錢
*/
public void insertQuarter();
/**
* 退回25分錢
*/
public void ejectQuarter();
/**
* 轉動曲柄
*/
public void turnCrank();
/**
* 發放糖果
*/
public void dispense();
}
(2)建立糖果機
將傳遞給糖果機的請求,委託給狀態類。
/**
* 糖果機
*/
public class GumballMachine {
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State state = soldOutState;
int gumballCount = 0;
public GumballMachine(int initGumballCount) {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
// 初始化糖果數量
this.gumballCount = initGumballCount;
// 初始化糖果機狀態
if (initGumballCount > 0) {
state = noQuarterState;
} else {
state = soldOutState;
}
}
/**
* 投入25分錢
*/
public void insertQuarter() {
state.insertQuarter();
}
/**
* 退回25分錢
*/
public void ejectQuarter() {
state.ejectQuarter();
}
/**
* 轉動曲柄
*/
public void turnCrank() {
state.turnCrank();
state.dispense();
}
/**
* 發放糖果
*/
public void releaseBall() {
System.out.println("A gumball comes rolling out the slot...");
if (gumballCount > 0) {
gumballCount = gumballCount -1;
}
}
public void setState(State state) {
this.state = state;
}
public int getGumballCount() {
return gumballCount;
}
public State getSoldOutState() {
return soldOutState;
}
public State getNoQuarterState() {
return noQuarterState;
}
public State getHasQuarterState() {
return hasQuarterState;
}
public State getSoldState() {
return soldState;
}
}
(3)建立具體的狀態,並實現狀態介面
/**
* 未投入25分錢狀態
*/
public class NoQuarterState implements State {
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
// 投入25分錢,並轉到已投入25分錢狀態
System.out.println("You inserted a quarter");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
@Override
public void ejectQuarter() {
// 當前為未投入25分錢狀態,不能退回25分錢
System.out.println("You haven't inserted a quarter");
}
@Override
public void turnCrank() {
// 當前為未投入25分錢狀態,不能轉動曲柄
System.out.println("You truned, but there's no quarter");
}
@Override
public void dispense() {
// 當前為未投入25分錢狀態,不能發放糖果
System.out.println("You need to pay first");
}
}
/**
* 已投入25分錢狀態
*/
public class HasQuarterState implements State {
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
// 當前為已投入25分錢狀態,不能再次投入
System.out.println("You can't insert another quarter");
}
@Override
public void ejectQuarter() {
// 退回25分錢,並將狀態轉到未投入25分錢狀態
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
@Override
public void turnCrank() {
// 轉動曲柄,並將狀態轉為售出狀態
System.out.println("You turned...");
gumballMachine.setState(gumballMachine.getSoldState());
}
@Override
public void dispense() {
// 當前為已投入25分錢狀態,還未轉動曲柄,不能發放糖果
System.out.println("No gumball dispensed");
}
}
/**
* 售出狀態
*/
public class SoldState implements State {
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
// 當前為售出狀態,不能再次投入25分錢
System.out.println("Please wait, we're already giving you a gumball");
}
@Override
public void ejectQuarter() {
// 當前為售出狀態,不能退回25分錢
System.out.println("Sorry, you already truned the crank");
}
@Override
public void turnCrank() {
// 當前為售出狀態,不能再次轉動曲柄
System.out.println("Turning twice doesn't get you another gumball!");
}
@Override
public void dispense() {
// 發放糖果
gumballMachine.releaseBall();
if (gumballMachine.getGumballCount() > 0) {
gumballMachine.setState(gumballMachine.getNoQuarterState());
} else {
System.out.println("Oops, out of gumballs!");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
}
/**
* 售罄狀態
*/
public class SoldOutState implements State {
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
// 當前為售罄狀態,不能投入25分錢
System.out.println("You can't insert a quarter, the machine is sold out");
}
@Override
public void ejectQuarter() {
// 當前為售罄狀態,不能要求退回25分錢
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
@Override
public void turnCrank() {
// 當前為售罄狀態,不能轉動曲柄
System.out.println("You turned, but there are no gumballs");
}
@Override
public void dispense() {
// 當前為售罄狀態,不能發放糖果
System.out.println("No gumball dispensed");
}
}
(4)操作糖果機
public class Test {
public static void main(String[] args) {
// 糖果機
GumballMachine gumballMachine = new GumballMachine(5);
// 正常操作
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println("-----------------------");
// 異常操作
gumballMachine.insertQuarter();
gumballMachine.ejectQuarter();
gumballMachine.turnCrank();
}
}