1. 程式人生 > >《Head First 設計模式》:狀態模式

《Head First 設計模式》:狀態模式

# 正文 ## 一、定義 狀態模式允許物件在內部狀態改變時改變它的行為,物件看起來好像修改了它的類。 **要點:** * 狀態模式允許一個物件基於內部狀態而擁有不同的行為。 * 狀態模式將狀態封裝成為獨立的類,並將動作委託到代表當前狀態的物件。 * 通過將每個狀態封裝進一個類,我們把以後需要做的任何改變區域性化了。 ## 二、實現步驟 ### 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 來實現糖果機的控制器。他們希望設計能夠儘量有彈性而且好維護,因為將來可能要為糖果機增加更多的行為。 糖果機的工作流程如下: ![](https://img2020.cnblogs.com/blog/1613877/202009/1613877-20200921224355099-3738421.jpg) ### 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();