状态模式
状态设计模式允许对象在内部状态改变时改变它的行为。
为什么要使用状态设计模式?
状态设计模式可将对象的状态行为封装到单独的状态对象中,并将对象的动作委托到当前状态的对象。通过这样的方式实现封装变化、对扩展开放、对修改关闭以增强代码可读性、扩展性、维护性的目的。
需求:万能糖果机公司委托设计一种糖果机的控制程序,以实现类似如下的状态控制流程功能。
st=>start: 开始使用糖果机 op=>operation: 投入硬币 cond=>condition: coins > 25? op2=>operation: 转动曲柄 op3=>operation: 售出糖果 op4=>operation: 退回投入的硬币 e=>end: 使用完毕
业务流程拥有至少3-4种可执行的动作分别是:投入硬币、退回硬币、转动曲柄、售出糖果。另外还具备3种(或者更多)状态对应可执行动作,分别是:等待投币、已投币、售罄状态。
要注意的是每次执行某种动作之前,必须对当前所处的状态进行检查。比如用户在投入硬币且消费硬币之前,无法再次投入硬币。
public class GumballMachine { // 售罄状态 final static int SOLD_OUT = 0; // 已投币状态 final static int HAS_QUARTER = 2; // 等待投币状态 final static int NO_QUARTER = 3; static int state = SOLD_OUT; static int count = 0; public GumballMachine(int count) { this.count = count; if (count > 0) { state = NO_QUARTER; } } /** * 投币函数 */ public void insertQuarter() { if (state == NO_QUARTER) { System.out.println("投入硬币成功"); state = HAS_QUARTER; } else if (state == SOLD_OUT) { System.out.println("已售罄,退回硬币"); } else if (state == HAS_QUARTER) { System.out.println("请勿重复投入硬币"); } else { System.out.println("未知的状态错误"); } } /** * 转动曲柄函数 */ public static void turnCrank() { if (state == NO_QUARTER) { System.out.println("还未投入硬币"); state = HAS_QUARTER; } else if (state == SOLD_OUT) { System.out.println("已售罄。退回硬币"); } else if (state == HAS_QUARTER) { System.out.println("正在发放糖果。。。。"); System.out.println("发放完毕"); state = NO_QUARTER; } else { System.out.println("未知的状态错误"); } } }
万能糖果机公司的产品经理们十分满意我们的工作成果,并希望在糖果机中的流程中增加一个功能。他们希望糖果机在投币后有几率“中奖”,在稍后转动曲柄发放糖果时有几率发放出双倍的糖果以加强糖果机的娱乐性,继而提升销售量。考虑如何对应这个变化:
- 首先,需要增加一种状态叫做中奖。
- 需要在每个动作函数中增加中奖状态的 check。
- 在 turnCrank 函数中需要判断当前是否是中奖状态,若是则要发放两倍糖果。并且在最后都需要把状态极的状态还原到最初的状态。
- 封装变化。
- 将状态的行为封装到状态对象中。
- 利用组合行为将状态变化委托到每个状态行为对象中去执行。
状态接口
/** * 状态接口 * * @author 12780 */ public interface State { /** * 投币 */ void insertQuarter(); /** * 转动曲柄 */ void turnCrank(); }
已投币,待出售状态
@Slf4j @Getter @Setter public class HasQuarterState implements State { private GumballMachine gumballMachine; public HasQuarterState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } @Override public void insertQuarter() { log.info("已投币,请先消费..."); } @Override public void turnCrank() { log.info("发放糖果..."); log.info("糖果发放完毕"); gumballMachine.setState(gumballMachine.getNoQuarterState()); } }
等待投币状态
@Slf4j @Getter @Setter public class NoQuarterState implements State { private GumballMachine gumballMachine; public NoQuarterState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } @Override public void insertQuarter() { log.info("投入硬币成功"); gumballMachine.setState(gumballMachine.getHasQuarterState()); } @Override public void turnCrank() { log.info("请投入硬币"); } }
售罄状态
@Slf4j @Getter @Setter public class SoldOutState implements State { private GumballMachine gumballMachine; public SoldOutState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine; } @Override public void insertQuarter() { log.info("已售罄"); } @Override public void turnCrank() { log.info("已售罄"); } }
利用委托方式将糖果机的任务委托给状态对象使用
@Getter @Setter public class GumballMachine { /** * 糖果机对应的所有状态 */ private State hasQuarterState; private State noQuarterState; private State soldOutState; /** * 糖果机目前状态 */ private State state; public GumballMachine() { hasQuarterState = new HasQuarterState(this); noQuarterState = new NoQuarterState(this); soldOutState = new SoldOutState(this); state = noQuarterState; } /** * 硬币投入 */ public void insertQuarter() { state.insertQuarter(); } /** * 转动曲柄 */ public void turnCrank() { state.turnCrank(); } public static void main(String[] args) { GumballMachine gumballMachine = new GumballMachine(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); } }
结果:
INFO com.design.mode.state.NoQuarterState - 请投入硬币 [main] INFO com.design.mode.state.NoQuarterState - 投入硬币成功 [main] INFO com.design.mode.state.HasQuarterState - 发放糖果... [main] INFO com.design.mode.state.HasQuarterState - 糖果发放完毕
对扩展开放,对修改关闭。若发生需求变更,仅仅需要的是增加一个状态对象,在状态对象处理好逻辑,而其他地方的代码你完全不需要改动。
立志如山 静心求实
浙公网安备 33010602011771号