10. Design Pattern State Pattern Take vending machine as an example

State mode gives me a bright feeling, it is worth learning~

First look at the definition: allowing an object to change its behavior when its internal state changes, and the object looks like it has modified its class. The definition begins to blur again. Just to understand, when the internal state of an object changes, its behavior changes with the state change, and it looks like a similar reinitialization.

Let's use an example to illustrate the usage of the state mode. Now there is a vending machine code that we need to write. The state diagram is as follows:

Insert picture description here

Analyze this state diagram:

a. Contains 4 states (we use 4 int constants to represent)

b. Contains 3 methods of exposure (coin insertion, coin withdrawal, turning handle)

c. We need to deal with the three actions that the user can trigger in each state.

Below we write the code based on the results of the analysis:

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;
 
		}
 
	}
}

For each action of the user, we have considered what happens in any state and have done certain processing. Here are some tests:

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();
 
	}
}

Output result:

成功投入硬币
退币成功
#------------
成功投入硬币
正在出商品....
发出商品...
----------压力测试-----
成功投入硬币
已经有硬币,无需投币
正在出商品....
发出商品...
请先投入硬币
您未投入硬币
请先投入硬币

It feels good and the functions are basically implemented, but some things are inevitable, that is, changes in demand. Now in order to increase sales, every time the user turns the handle to buy a product, there is a 10% chance of giving away a bottle.
The current state diagram has changed. When the user turns the handle, a winning state may be reached: the diagram is as follows:

Insert picture description here

If you add directly to the code we just wrote, you need to add a judgment condition to the switch of each action, and it is very error-prone. So now we have to consider redesigning our code. We consider writing each state into a state class, which is responsible for implementing the behavior under the corresponding action, and then the vending machine switches between the incapable states:

Let's start the reconstruction. We now have 5 states, corresponding to 4 actions (insert coin, refund, turn handle, send out goods), the following first defines a super type of state:

package com.zhy.pattern.status.b;
 
/**
 * 状态的接口
 * @author zhy
 *
 */
public interface State
{
	/**
	 * 放钱
	 */
	public void insertMoney();
	/**
	 * 退钱
	 */
	public void backMoney();
	/**
	 * 转动曲柄
	 */
	public void turnCrank();
	/**
	 * 出商品
	 */
	public void dispense();
}

Then the realization of each state:

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());
			}
			
		}
 
	}
 
}

Finally, the code of the vending machine:

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 ...
 
}

It can be seen that we now localize the behavior of each state corresponding to the action into the state's own class, which not only increases the scalability but also greatly improves the readability of the code. To add a state later, you only need to implement the newly added state and add this state to the vending machine.
Here are some tests:

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();
 
	}
}

Output result:

投币成功
退币成功
----我要中奖----
投币成功
你转动了手柄
发出一件商品...
投币成功
你转动了手柄
发出一件商品...
投币成功
你转动了手柄
发出一件商品...
投币成功
你转动了手柄
发出一件商品...
投币成功
你转动了手柄
发出一件商品...
投币成功
你转动了手柄
发出一件商品...
投币成功
你转动了手柄
你中奖了,恭喜你,将得到2件商品
发出一件商品...
发出一件商品...
-------压力测试------
投币成功
退币成功
您未投币,想退钱?...
您未投币,想拿东西么?...
您未投币,想拿东西么?...
您未投币,想退钱?...

Congratulations, you have learned a design pattern, the state pattern. Finally, look at the class diagram of the state pattern:

Insert picture description here

Finally, everyone is welcome to leave a message~

Guess you like

Origin blog.csdn.net/GTC_GZ/article/details/108752419