1.定义
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类.
和策略模式有点像,都是封装了行为,不同的是状态模式多了状态,根据不同的状态来实行不同的行为.
2.代码实现
以糖果机为例子,糖果机分别对应 1. 没有钱 2. 有钱 3. 售出糖果 4. 糖果售罄 5. 胜利者 这四种状态,以及 1. 投入钱 2. 转动曲柄 3. 退回25分钱 4. 发放糖果 这四种行为
因为每种状态对应的糖果机行为都不一样,我们直接使用状态模式来实现
定义 状态类 State
package state; public interface State { /** * 投钱 */ void insertQuarter(); /** * 退钱 */ void ejectQuarter(); /** * 转动曲柄 */ void turnCrank(); /** * 发放糖果 */ void dispense(); }
状态接口中定义了四种行为,我们可以实现不同的状态来实现每种状态对应的行为
定义没有钱 状态
package state; public class NoQuarterState implements State { GumballMachine gumballMachine; public NoQuarterState(GumballMachine gumballMachine) { super(); this.gumballMachine = gumballMachine; } @Override public void insertQuarter() { System.out.println("You inserted a quarter"); gumballMachine.setState(gumballMachine.getHasQuarterState()); } @Override public void ejectQuarter() { System.out.println("You havent't inserted a quarter"); } @Override public void turnCrank() { System.out.println("You turned, but there's no quarter"); } @Override public void dispense() { System.out.println("You need to pay first"); } @Override public String toString() { return "NoQuarterState []"; } }
在insertQuarter方法中,我们投入钱后会改变状态为HasQuarterState
定义有钱状态
package state; import java.util.Random; public class HasQuarterState implements State{ Random randomWinner = new Random(System.currentTimeMillis()); GumballMachine gumballMachine; public HasQuarterState(GumballMachine gumballMachine) { super(); this.gumballMachine = gumballMachine; } @Override public void insertQuarter() { System.out.println("You can't insert another quarter"); } @Override public void ejectQuarter() { System.out.println("Quarter returned"); gumballMachine.setState(gumballMachine.getNoQuarterState()); } @Override public void turnCrank() { System.out.println("You turned..."); int winner = randomWinner.nextInt(10); if ((winner == 0) && (gumballMachine.getCount() > 1)) { gumballMachine.setState(gumballMachine.getWinnerState()); } else { gumballMachine.setState(gumballMachine.getSoldState()); } } @Override public void dispense() { System.out.println("No gumball dispensed"); } @Override public String toString() { return "HasQuarterState []"; } }
当投入钱后我们需要转动手柄摇出糖果,这边多了一个状态叫做胜利者状态,当随机数为0的时候会发送两颗糖果
定义贩卖状态
package state; public class SoldState implements State{ GumballMachine gumballMachine; public SoldState(GumballMachine gumballMachine) { super(); this.gumballMachine = gumballMachine; } @Override public void insertQuarter() { System.out.println("Please wait, we're already giving you a gumball"); } @Override public void ejectQuarter() { System.out.println("Sorry, you already turned 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.getCount() > 0) { gumballMachine.setState(gumballMachine.getNoQuarterState()); } else { System.out.println("Oops, out of gumballs!"); gumballMachine.setState(gumballMachine.getSoldOutState()); } } @Override public String toString() { return "SoldState []"; } }
在dispense方法中,当发出糖果后,糖果数量为0则设置为SoldOut售罄状态,否则设置为NoQuarter状态,等待下一次投钱.
定义SoldOut售罄状态
package state; public class SoldOutState implements State{ GumballMachine gumballMachine; public SoldOutState(GumballMachine gumballMachine) { super(); this.gumballMachine = gumballMachine; } @Override public void insertQuarter() { System.out.println("no gumball"); } @Override public void ejectQuarter() { System.out.println("no gumball"); } @Override public void turnCrank() { System.out.println("no gumball"); } @Override public void dispense() { System.out.println("no gumball"); } @Override public String toString() { return "SoldOutState []"; } }
售罄状态的四种行为不会执行任何操作,因为没有糖果,需要等待投入糖果后才能改变这个状态,但是这里没有实现投入糖果这种行为.
定义胜利者状态
package state; public class WinnerState implements State{ GumballMachine gumballMachine; public WinnerState(GumballMachine gumballMachine) { super(); this.gumballMachine = gumballMachine; } @Override public void insertQuarter() { System.out.println("error"); } @Override public void ejectQuarter() { System.out.println("error"); } @Override public void turnCrank() { System.out.println("error"); } @Override public void dispense() { System.out.println("You're a winner! You get two gumballs for your quarter"); gumballMachine.releaseBall(); if (gumballMachine.getCount() == 0) { gumballMachine.setState(gumballMachine.getSoldOutState()); } else { gumballMachine.releaseBall(); if (gumballMachine.getCount() == 0) { gumballMachine.setState(gumballMachine.getSoldOutState()); } else { System.out.println("Oops, out of gumballs!"); gumballMachine.setState(gumballMachine.getSoldOutState()); } } } @Override public String toString() { return "WinnerState []"; } }
在dispense方法中,我们发放两颗糖果,并且通过每次发完糖果都要判断是否为0,所以if else 多一些.
接下来定义糖果机,因为使用了状态模式,所以代码简洁一点,因为实现都封装在了各自的State状态中.直接根据每种状态调用行为即可.
package state; import java.util.concurrent.SynchronousQueue; public class GumballMachine { 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; } } public void insertQuarter() { state.insertQuarter(); } public void ejectQuarter() { state.ejectQuarter(); } public void turnCrank() { state.turnCrank(); state.dispense(); } public void releaseBall() { System.out.println("A gumball come rolling out the slot..."); if (count != 0) { count--; } } public State getSoldOutState() { return soldOutState; } public void setSoldOutState(State soldOutState) { this.soldOutState = soldOutState; } public State getNoQuarterState() { return noQuarterState; } public void setNoQuarterState(State noQuarterState) { this.noQuarterState = noQuarterState; } public State getHasQuarterState() { return hasQuarterState; } public void setHasQuarterState(State hasQuarterState) { this.hasQuarterState = hasQuarterState; } public State getSoldState() { return soldState; } public void setSoldState(State soldState) { this.soldState = soldState; } public State getState() { return state; } public void setState(State state) { this.state = state; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public State getWinnerState() { return winnerState; } public void setWinnerState(State winnerState) { this.winnerState = winnerState; } @Override public String toString() { return "GumballMachine [state=" + state + ", count=" + count + "]"; } }
测试类
package state; public class GumballMachineTestDriver { public static void main(String[] args) { GumballMachine gumballMachine = new GumballMachine(5); System.out.println(gumballMachine); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); System.out.println(gumballMachine); } }
GumballMachine [state=NoQuarterState [], count=5] You inserted a quarter You turned... A gumball come rolling out the slot... GumballMachine [state=NoQuarterState [], count=4] You inserted a quarter You turned... You're a winner! You get two gumballs for your quarter A gumball come rolling out the slot... A gumball come rolling out the slot... Oops, out of gumballs! no gumball no gumball no gumball GumballMachine [state=SoldOutState [], count=2]
3.总结
状态模式和策略差不多,只是多了状态而已,如果没有状态的改变建议还是使用策略模式,而且状态可以在 Context (也就是我们定义的糖果机) 中改变,也可以在定义的状态中改变,这里因为业务需要在运行时改变,所以定义在State中会好一些.