转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/26350617
状态模式给了我眼前一亮的感觉啊,值得学习~
先看定义:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。定义又开始模糊了,理一下,当对象的内部状态改变时,它的行为跟随状态的改变而改变了,看起来好像重新初始化了一个类似的。
下面使用个例子来说明状态模式的用法,现在有个自动售货机的代码需要我们来写,状态图如下:
分析一个这个状态图:
a、包含4个状态(我们使用4个int型常量来表示)
b、包含3个暴露在外的方法(投币、退币、转动手柄)
c、我们需要处理每个状态下,用户都可以触发这三个动作。
下面我们根据分析的结果,写出代码:
package com.zhy.pattern.status; /** * 自动售货机 * * @author zhy * */ public class VendingMachine { /** * 已投币 */ private final static int HAS_MONEY = 0; /** * 未投币 */ private final static int NO_MONEY = 1; /** * 售出商品 */ private final static int SOLD = 2; /** * 商品售罄 */ private final static int SOLD_OUT = 3; private int currentStatus = NO_MONEY; /** * 商品数量 */ private int count = 0; public VendingMachine(int count) { this.count = count; if (count > 0) { currentStatus = NO_MONEY; } } /** * 投入硬币,任何状态用户都可能投币 */ public void insertMoney() { switch (currentStatus) { case NO_MONEY: currentStatus = HAS_MONEY; System.out.println("成功投入硬币"); break; case HAS_MONEY: System.out.println("已经有硬币,无需投币"); break; case SOLD: System.out.println("请稍等..."); break; case SOLD_OUT: System.out.println("商品已经售罄,请勿投币"); break; } } /** * 退币,任何状态用户都可能退币 */ public void backMoney() { switch (currentStatus) { case NO_MONEY: System.out.println("您未投入硬币"); break; case HAS_MONEY: currentStatus = NO_MONEY; System.out.println("退币成功"); break; case SOLD: System.out.println("您已经买了糖果..."); break; case SOLD_OUT: System.out.println("您未投币..."); break; } } /** * 转动手柄购买,任何状态用户都可能转动手柄 */ public void turnCrank() { switch (currentStatus) { case NO_MONEY: System.out.println("请先投入硬币"); break; case HAS_MONEY: System.out.println("正在出商品...."); currentStatus = SOLD; dispense(); break; case SOLD: System.out.println("连续转动也没用..."); break; case SOLD_OUT: System.out.println("商品已经售罄"); break; } } /** * 发放商品 */ private void dispense() { switch (currentStatus) { case NO_MONEY: case HAS_MONEY: case SOLD_OUT: throw new IllegalStateException("非法的状态..."); case SOLD: count--; System.out.println("发出商品..."); if (count == 0) { System.out.println("商品售罄"); currentStatus = SOLD_OUT; } else { currentStatus = NO_MONEY; } break; } } }
package com.zhy.pattern.status; public class TestTra { public static void main(String[] args) { VendingMachine machine = new VendingMachine(10); machine.insertMoney(); machine.backMoney(); System.out.println("-----------"); machine.insertMoney(); machine.turnCrank(); System.out.println("----------压力测试-----"); machine.insertMoney(); machine.insertMoney(); machine.turnCrank(); machine.turnCrank(); machine.backMoney(); machine.turnCrank(); } }输出结果:
成功投入硬币 退币成功 ----------- 成功投入硬币 正在出商品.... 发出商品... ----------压力测试----- 成功投入硬币 已经有硬币,无需投币 正在出商品.... 发出商品... 请先投入硬币 您未投入硬币 请先投入硬币感觉还是不错的,基本实现了功能,但是有些事情是不可避免的,那就是需求的变化,现在为了提升销量,当用户每次转动手柄买商品的时候,有10%的几率赠送一瓶。
现在的状态图发生了变化,当用户转动手柄时,可能会达到一个中奖的状态:图如下:
如果在我们刚写的代码上直接添加,则需要在每个动作的switch中添加判断条件,且非常容易出错。所以现在我们要考虑重新设计我们的代码,我们考虑把每个状态写状态类,负责实现在对应动作下的行为,然后自动售货机在不能的状态间切换:
下面开始重构,我们现在有5种状态,对应4个动作(投币、退币、转动手柄、发出商品),下面首先定义一个状态的超类型:
package com.zhy.pattern.status.b; /** * 状态的接口 * @author zhy * */ public interface State { /** * 放钱 */ public void insertMoney(); /** * 退钱 */ public void backMoney(); /** * 转动曲柄 */ public void turnCrank(); /** * 出商品 */ public void dispense(); }
package com.zhy.pattern.status.b; /** * 没钱的状态 * @author zhy * */ public class NoMoneyState implements State { private VendingMachine machine; public NoMoneyState(VendingMachine machine) { this.machine = machine; } @Override public void insertMoney() { System.out.println("投币成功"); machine.setState(machine.getHasMoneyState()); } @Override public void backMoney() { System.out.println("您未投币,想退钱?..."); } @Override public void turnCrank() { System.out.println("您未投币,想拿东西么?..."); } @Override public void dispense() { throw new IllegalStateException("非法状态!"); } }
package com.zhy.pattern.status.b; import java.util.Random; /** * 已投入钱的状态 * * @author zhy * */ public class HasMoneyState implements State { private VendingMachine machine; private Random random = new Random(); public HasMoneyState(VendingMachine machine) { this.machine = machine; } @Override public void insertMoney() { System.out.println("您已经投过币了,无需再投...."); } @Override public void backMoney() { System.out.println("退币成功"); machine.setState(machine.getNoMoneyState()); } @Override public void turnCrank() { System.out.println("你转动了手柄"); int winner = random.nextInt(10); if (winner == 0 && machine.getCount() > 1) { machine.setState(machine.getWinnerState()); } else { machine.setState(machine.getSoldState()); } } @Override public void dispense() { throw new IllegalStateException("非法状态!"); } }
package com.zhy.pattern.status.b; /** * 售罄的状态 * * @author zhy * */ public class SoldOutState implements State { private VendingMachine machine; public SoldOutState(VendingMachine machine) { this.machine = machine; } @Override public void insertMoney() { System.out.println("投币失败,商品已售罄"); } @Override public void backMoney() { System.out.println("您未投币,想退钱么?..."); } @Override public void turnCrank() { System.out.println("商品售罄,转动手柄也木有用"); } @Override public void dispense() { throw new IllegalStateException("非法状态!"); } }
package com.zhy.pattern.status.b; /** * 准备出商品的状态,该状态下,不会有任何用户的操作 * * @author zhy * */ public class SoldState implements State { private VendingMachine machine; public SoldState(VendingMachine machine) { this.machine = machine; } @Override public void insertMoney() { System.out.println("正在出货,请勿投币"); } @Override public void backMoney() { System.out.println("正在出货,没有可退的钱"); } @Override public void turnCrank() { System.out.println("正在出货,请勿重复转动手柄"); } @Override public void dispense() { machine.dispense(); if (machine.getCount() > 0) { machine.setState(machine.getNoMoneyState()); } else { System.out.println("商品已经售罄"); machine.setState(machine.getSoldOutState()); } } }
package com.zhy.pattern.status.b; /** * 中奖的状态,该状态下不会有任何用户的操作 * * @author zhy * */ public class WinnerState implements State { private VendingMachine machine; public WinnerState(VendingMachine machine) { this.machine = machine; } @Override public void insertMoney() { throw new IllegalStateException("非法状态"); } @Override public void backMoney() { throw new IllegalStateException("非法状态"); } @Override public void turnCrank() { throw new IllegalStateException("非法状态"); } @Override public void dispense() { System.out.println("你中奖了,恭喜你,将得到2件商品"); machine.dispense(); if (machine.getCount() == 0) { System.out.println("商品已经售罄"); machine.setState(machine.getSoldOutState()); } else { machine.dispense(); if (machine.getCount() > 0) { machine.setState(machine.getNoMoneyState()); } else { System.out.println("商品已经售罄"); machine.setState(machine.getSoldOutState()); } } } }
package com.zhy.pattern.status.b; /** * 自动售货机 * * @author zhy * */ public class VendingMachine { private State noMoneyState; private State hasMoneyState; private State soldState; private State soldOutState; private State winnerState ; private int count = 0; private State currentState = noMoneyState; public VendingMachine(int count) { noMoneyState = new NoMoneyState(this); hasMoneyState = new HasMoneyState(this); soldState = new SoldState(this); soldOutState = new SoldOutState(this); winnerState = new WinnerState(this); if (count > 0) { this.count = count; currentState = noMoneyState; } } public void insertMoney() { currentState.insertMoney(); } public void backMoney() { currentState.backMoney(); } public void turnCrank() { currentState.turnCrank(); if (currentState == soldState || currentState == winnerState) currentState.dispense(); } public void dispense() { System.out.println("发出一件商品..."); if (count != 0) { count -= 1; } } public void setState(State state) { this.currentState = state; } //getter setter omitted ... }
下面进行一些测试:
package com.zhy.pattern.status.b; public class Test { public static void main(String[] args) { VendingMachine machine = new VendingMachine(10); machine.insertMoney(); machine.backMoney(); System.out.println("----我要中奖----"); machine.insertMoney(); machine.turnCrank(); machine.insertMoney(); machine.turnCrank(); machine.insertMoney(); machine.turnCrank(); machine.insertMoney(); machine.turnCrank(); machine.insertMoney(); machine.turnCrank(); machine.insertMoney(); machine.turnCrank(); machine.insertMoney(); machine.turnCrank(); System.out.println("-------压力测试------"); machine.insertMoney(); machine.backMoney(); machine.backMoney(); machine.turnCrank();// 无效操作 machine.turnCrank();// 无效操作 machine.backMoney(); } }
投币成功 退币成功 ----我要中奖---- 投币成功 你转动了手柄 发出一件商品... 投币成功 你转动了手柄 发出一件商品... 投币成功 你转动了手柄 发出一件商品... 投币成功 你转动了手柄 发出一件商品... 投币成功 你转动了手柄 发出一件商品... 投币成功 你转动了手柄 发出一件商品... 投币成功 你转动了手柄 你中奖了,恭喜你,将得到2件商品 发出一件商品... 发出一件商品... -------压力测试------ 投币成功 退币成功 您未投币,想退钱?... 您未投币,想拿东西么?... 您未投币,想拿东西么?... 您未投币,想退钱?...
最后,欢迎大家留言~
设计模式 状态模式 以自动售货机为例,布布扣,bubuko.com
原文地址:http://blog.csdn.net/lmj623565791/article/details/26350617