设计模式 状态模式 以自动售货机为例(一)

2014-11-23 20:19:57 · 作者: · 浏览: 35

转载请标明出处: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%的几率赠送一瓶。

现在的状态图