1、状态模式定义
2、状态模式类图
3、状态模式案例:糖果机
3.1、需求
3.2、老办法实现
3.3、使用状态模式实现
3.4、状态模式实现糖果机代码
3.5、状态模式的优点
1、状态模式定义 <--返回目录
状态模式定义:允许对象再内部状态改变时改变它的行为,对象看起来就好像修改了它的类。
状态模式的理解:使用组合封装基于状态的行为,并将行为委托到当前状态。
这个描述的第一部分有相当多的含义,因为这个模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象,我们知道行为回随着内部状态而改变。
定义中的第二部分:一个对象"看起来好像修改了它的类"是什么意思?从客户的视角来看,如果说你使用的对象能够完全改变它的行为,那么你会觉得,这个对象实际上是从别的类实例化而来的。然而,实际上,你知道我们是再使用组合通过简单引用不同的状态对象来造成类改变的假象。
2、状态模式类图 <--返回目录
状态模式的类图和策略模式的类图一模一样,但是这两个模式的差别在于它们的"意图"。
以状态模式而言,我们将一群行为封装在状态对象中,context的行为随时可委托到哪些状态对象中的一个。随着时间的流逝,当前状态在状态对象集合中游走改变,以反映出context内部的状态。因此,context的行为也会跟着改变,但是context的客户对于状态对象了解不多,甚至是浑然不觉。
而以策略模式而言,客户通常主动指定context所要组合的策略对象是哪一个。现在,固然策略模式让我们具有弹性,能够在运行时改变策略,但对于某个context对象来说,通常只有一个最适合的策略对象。
一般来说,我们把策略模式想成是除了继承之外的一种弹性替代方案。如果你使用继承定义了一个类的行为,你将被这个行为困住,甚至要修改它都很难。有了策略模式,你可以通过组合不同的对象来改变行为。
我们把状态模式想成是不用在context中放置许多条件判断的替代方案。通过将行为包装进状态对象中,你可以通过在context内简单地改变状态对象来改变context的行为。
3、状态模式案例:糖果机 <--返回目录
3.1、需求 <--返回目录
3.2、老办法实现 <--返回目录
package com.oy; /** * 实现糖果机。利用实例变量持有当前的状态,然后需要处理所有可能发生的动作、行为和状态的转换。 * 我们需要实现的动作包括:投入25分钱、退回25分钱、转动曲柄和发放糖果,也要检查糖果是否售罄。 * @author oy * @date 2020年9月6日 下午9:13:38 * @version 1.0.0 */ public class GumballMachine { final static int SOLD_OUT = 0; // 售罄 final static int NO_QUARTER = 1; // 没有投入25分钱 final static int HAS_QUARTER = 2; // 有投入25分钱 final static int SOLD = 3; // 售出 // 跟踪当前状态,一开始被设置为 "糖果售罄" int state = SOLD_OUT; // 跟踪机器内的糖果数目 int count = 0; /** * 构造器。需要初始糖果库存量作为参数。如果库存量不为0的话,机器就会进入"没有25分钱"的状态, * 也就是说它等着别人投入25分钱。如果糖果数目为0的话,机器就会保持在"糖果售罄"的状态。 * @param count */ public GumballMachine(int count) { this.count = count; if (count > 0) { state = NO_QUARTER; } } /** * 当有25分钱透进来,就会执行该方法 */ 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"); } } /** * 顾客试着退回25分钱 */ public void ejectQuarter() { if (state == HAS_QUARTER) { // 如果有25分钱,就把钱退出,回到"没有25分钱"的状态 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"); } } public void turnCrank() { if (state == SOLD) { System.out.println("Turning twice doesn't get you another gumball!"); } else if (state == NO_QUARTER) { System.out.println("You turned but there's no quarter"); } else if (state == SOLD_OUT) { System.out.println("You turned, but there are no gumballs"); } else if (state == HAS_QUARTER) { // 已经投入25分钱,顾客转动曲柄,改变状态为"售出糖果",然后调用机器的dispense()方法 System.out.println("You turned..."); state = SOLD; dispense(); // 调用此方法,发放糖果 } } /** * 调用此方法,发放糖果 */ public void dispense() { if (state == SOLD) { System.out.println("A gumball comes rolling out the slot"); count = count - 1; // 检查糖果是否售罄 if (count == 0) { System.out.println("Oops, out of gumballs!"); } else { state = NO_QUARTER; } } // 这些都不应该发生。这里仍然进行判断。 else if (state == NO_QUARTER) { System.out.println("You need to pay first"); } else if (state == SOLD_OUT) { System.out.println("No gumball dispensed"); } else if (state == HAS_QUARTER) { System.out.println("No gumball dispensed"); } } // 这里是像 toString()和refill()的其他方法 }
上面的实现的问题在于:扩展性不好。一旦有新需求,添加一个状态,if/else就全部要修改,而且容易出现bug。也就是说,一旦需要扩展,就需要破坏旧有的结构,还有可能引入bug。
3.3、使用状态模式实现 <--返回目录
3.4、状态模式实现糖果机代码 <--返回目录
GumballMachine
package com.oy; import com.oy.state.HasQuarterState; import com.oy.state.NoQuarterState; import com.oy.state.SoldOutState; import com.oy.state.SoldState; import com.oy.state.State; /** * 状态模式实现糖果机。 * @author oy * @date 2020年9月6日 下午9:13:38 * @version 1.0.0 */ public class GumballMachine { State soldOutState; // 售罄 State noQuarterState; // 没有投入25分钱 State hasQuarterState; // 有投入25分钱 State soldState; // 售出 // 跟踪当前状态,一开始被设置为 "糖果售罄" State state = soldOutState; // 跟踪机器内的糖果数目 int count = 0; /** * 构造器。需要初始糖果库存量作为参数。如果库存量不为0的话,机器就会进入"没有25分钱"的状态, * 也就是说它等着别人投入25分钱。如果糖果数目为0的话,机器就会保持在"糖果售罄"的状态。 * @param count */ public GumballMachine(int count) { soldOutState = new SoldOutState(this); noQuarterState = new NoQuarterState(this); hasQuarterState = new HasQuarterState(this); soldState = new SoldState(this); this.count = count; if (count > 0) { state = noQuarterState; } } /** * 当有25分钱透进来,就会执行该方法 */ public void insertQuarter() { state.insertQuarter(); } /** * 顾客试着退回25分钱 */ 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; } } 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; } }
接口 State
package com.oy.state; public interface State { void insertQuarter(); void ejectQuarter(); void turnCrank(); void dispense(); }
State 的实现 NoQuarterState
package com.oy.state; import com.oy.GumballMachine; public class NoQuarterState implements State { GumballMachine gumballMachine; public NoQuarterState(GumballMachine gumballMachine) { 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 haven'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"); } }
其他状态实现类省略。
3.5、状态模式的优点 <--返回目录
---