糖果机的状态转换。。
一台糖果机,支持n种操作,譬如:投入硬币、退还硬币、转动曲柄、填充糖果……
而不同状态下不同操作返回的内容不一样。下面是它的状态转换图:
-
package state.old;
-
-
public class OldCandyMachine {
-
-
private int state;
-
public static int EMPTY_WORK_STATE = 0;
-
public static int FREE_WORK_STATE = 1;
-
public static int HAVE_MONEY_WORK_STATE = 2;
-
-
private int candyCount;
-
-
public OldCandyMachine(int candyCount){
-
this.candyCount = candyCount;
-
if(candyCount>0){
-
state = OldCandyMachine.FREE_WORK_STATE;
-
}else{
-
state = OldCandyMachine.EMPTY_WORK_STATE;
-
}
-
}
-
-
public int getCandyCount() {
-
return candyCount;
-
}
-
-
public void setCandyCount(int candyCount){
-
this.candyCount = candyCount;
-
}
-
-
public boolean fillCandy(int candyCount){
-
if(state == OldCandyMachine.EMPTY_WORK_STATE){
-
this.candyCount = candyCount;
-
this.state = OldCandyMachine.FREE_WORK_STATE;
-
return true;
-
}
-
return false;
-
}
-
-
public boolean inputMoney(){
-
if( state == OldCandyMachine.FREE_WORK_STATE ){
-
state = OldCandyMachine.HAVE_MONEY_WORK_STATE;
-
return true;
-
}
-
if( state == OldCandyMachine.EMPTY_WORK_STATE ){
-
return false;
-
}
-
if( state == OldCandyMachine.HAVE_MONEY_WORK_STATE ){
-
return false;
-
}
-
return false;
-
}
-
-
public boolean returnMoney(){
-
if( state == OldCandyMachine.HAVE_MONEY_WORK_STATE ){
-
state = OldCandyMachine.FREE_WORK_STATE;
-
return true;
-
}
-
return false;
-
}
-
-
public boolean turn(){
-
if( state == OldCandyMachine.HAVE_MONEY_WORK_STATE ){
-
state = OldCandyMachine.FREE_WORK_STATE;
-
outCandy();
-
return true;
-
}
-
return false;
-
}
-
-
public void outCandy(){
-
candyCount--;
-
if(candyCount==0){
-
state = OldCandyMachine.EMPTY_WORK_STATE;
-
}
-
}
-
-
}
在这样的代码下,如果我们要增加一个状态,该修改多少代码?
它的条件语句过多,容易出错,状态转换并不明显,
下面,我们尝试着用状态模式来重构。
[CandyMachine]使用了状态模式的、崭新的糖果机
-
package state;
-
-
import state.workstate.EmptyWorkState;
-
import state.workstate.FreeWorkState;
-
import state.workstate.WorkState;
-
-
-
public class CandyMachine {
-
-
private WorkState workState;
-
-
private int candyCount;
-
-
public CandyMachine(int candyCount){
-
this.candyCount = candyCount;
-
if(candyCount>0){
-
workState = new FreeWorkState(this);
-
}else{
-
workState = new EmptyWorkState(this);
-
}
-
}
-
-
public int getCandyCount() {
-
return candyCount;
-
}
-
-
public void setCandyCount(int candyCount){
-
this.candyCount = candyCount;
-
}
-
-
public WorkState getWorkState() {
-
return workState;
-
}
-
-
public void setWorkState(WorkState workState) {
-
this.workState = workState;
-
}
-
-
public boolean fillCandy(int candyCount){
-
if(workState.fillCandy()){
-
this.candyCount = candyCount;
-
return true;
-
}
-
return false;
-
}
-
-
public boolean inputMoney(){
-
return workState.inputMoney();
-
}
-
-
public boolean returnMoney(){
-
return workState.returnMoney();
-
}
-
-
public boolean turn(){
-
if(workState.turn()){
-
outCandy();
-
return true;
-
}
-
return false;
-
}
-
-
public void outCandy(){
-
candyCount--;
-
if(candyCount==0){
-
this.setWorkState(new EmptyWorkState(this));
-
}
-
}
-
-
}
[WorkState]糖果机工作状态:抽象类,也可定义成接口,在这里约定要实现的方法(糖果机能进行的操作)。
-
package state.workstate;
-
-
import state.CandyMachine;
-
-
-
public abstract class WorkState {
-
-
public CandyMachine machine;
-
-
public WorkState(CandyMachine machine){
-
this.machine = machine;
-
}
-
-
public abstract boolean fillCandy();
-
-
public abstract boolean inputMoney();
-
-
public abstract boolean returnMoney();
-
-
public abstract boolean turn();
-
-
}
[EmptyWorkState]糖果售罄的糖果机工作状态
-
package state.workstate;
-
-
import state.CandyMachine;
-
-
-
public class EmptyWorkState extends WorkState {
-
-
public EmptyWorkState(CandyMachine machine) {
-
super(machine);
-
}
-
-
@Override
-
public boolean fillCandy() {
-
machine.setWorkState(new FreeWorkState(machine));
-
return true;
-
}
-
-
@Override
-
public boolean inputMoney() {
-
return false;
-
}
-
-
@Override
-
public boolean returnMoney() {
-
return false;
-
}
-
-
@Override
-
public boolean turn() {
-
return false;
-
}
-
-
}
[FreeWorkState]空闲的糖果机工作状态
-
package state.workstate;
-
-
import state.CandyMachine;
-
-
-
public class FreeWorkState extends WorkState {
-
-
public FreeWorkState(CandyMachine machine) {
-
super(machine);
-
}
-
-
@Override
-
public boolean fillCandy() {
-
return false;
-
}
-
-
@Override
-
public boolean inputMoney() {
-
machine.setWorkState(new HaveMoneyWorkState(machine));
-
return true;
-
}
-
-
@Override
-
public boolean returnMoney() {
-
return false;
-
}
-
-
@Override
-
public boolean turn() {
-
return false;
-
}
-
-
}
[HaveMoneyWorkState]已填充硬币后的糖果机工作状态
-
package state.workstate;
-
-
import state.CandyMachine;
-
-
-
public class HaveMoneyWorkState extends WorkState {
-
-
public HaveMoneyWorkState(CandyMachine machine) {
-
super(machine);
-
}
-
-
@Override
-
public boolean fillCandy() {
-
return false;
-
}
-
-
@Override
-
public boolean inputMoney() {
-
return false;
-
}
-
-
@Override
-
public boolean returnMoney() {
-
machine.setWorkState(new FreeWorkState(machine));
-
return true;
-
}
-
-
@Override
-
public boolean turn() {
-
machine.setWorkState(new FreeWorkState(machine));
-
return true;
-
}
-
-
}
以及它的UT
-
package state;
-
-
import state.old.OldCandyMachine;
-
import state.workstate.*;
-
import junit.framework.TestCase;
-
-
public class TestCandyMachineState extends TestCase {
-
-
public void testOldCandyMachine(){
-
OldCandyMachine machine = new OldCandyMachine(0);
-
-
assertFalse(machine.inputMoney());
-
assertFalse(machine.returnMoney());
-
assertFalse(machine.turn());
-
assertTrue(machine.fillCandy(3)); // 此处对糖果机填充:状态已经改变
-
-
assertEquals(machine.getCandyCount(),3); // 填充三个
-
-
assertFalse(machine.returnMoney());
-
assertFalse(machine.fillCandy(3));
-
assertFalse(machine.turn());
-
assertTrue(machine.inputMoney()); // 此处将钱放入糖果机:状态已经改变
-
-
assertFalse(machine.inputMoney());
-
assertFalse(machine.fillCandy(3));
-
assertTrue(machine.returnMoney()); // 此处从糖果机退钱:状态已经改变
-
-
assertFalse(machine.returnMoney());
-
assertFalse(machine.fillCandy(3));
-
assertFalse(machine.turn());
-
assertTrue(machine.inputMoney()); // 此处将钱放入糖果机:状态已经改变
-
-
assertFalse(machine.inputMoney());
-
assertFalse(machine.fillCandy(3));
-
assertTrue(machine.turn()); // 此处使用糖果机:状态已经改变
-
-
assertEquals(machine.getCandyCount(),2); // 使用过一次后数量-1
-
}
-
-
public void testCandyMachine(){
-
CandyMachine machine = new CandyMachine(0);
-
-
assertFalse(machine.inputMoney());
-
assertFalse(machine.returnMoney());
-
assertFalse(machine.turn());
-
assertTrue(machine.getWorkState() instanceof EmptyWorkState );
-
assertTrue(machine.fillCandy(3)); // 此处对糖果机填充:状态已经改变
-
assertTrue(machine.getWorkState() instanceof FreeWorkState );
-
-
assertEquals(machine.getCandyCount(),3); // 填充三个
-
-
assertFalse(machine.returnMoney());
-
assertFalse(machine.fillCandy(3));
-
assertFalse(machine.turn());
-
assertTrue(machine.getWorkState() instanceof FreeWorkState );
-
assertTrue(machine.inputMoney()); // 此处将钱放入糖果机:状态已经改变
-
assertTrue(machine.getWorkState() instanceof HaveMoneyWorkState );
-
-
assertFalse(machine.inputMoney());
-
assertFalse(machine.fillCandy(3));
-
assertTrue(machine.getWorkState() instanceof HaveMoneyWorkState );
-
assertTrue(machine.returnMoney()); // 此处从糖果机退钱:状态已经改变
-
assertTrue(machine.getWorkState() instanceof FreeWorkState );
-
-
assertFalse(machine.returnMoney());
-
assertFalse(machine.fillCandy(3));
-
assertFalse(machine.turn());
-
assertTrue(machine.getWorkState() instanceof FreeWorkState );
-
assertTrue(machine.inputMoney()); // 此处将钱放入糖果机:状态已经改变
-
assertTrue(machine.getWorkState() instanceof HaveMoneyWorkState );
-
-
assertFalse(machine.inputMoney());
-
assertFalse(machine.fillCandy(3));
-
assertTrue(machine.getWorkState() instanceof HaveMoneyWorkState );
-
assertTrue(machine.turn()); // 此处使用糖果机:状态已经改变
-
assertTrue(machine.getWorkState() instanceof FreeWorkState );
-
-
assertEquals(machine.getCandyCount(),2); // 使用过一次后数量-1
-
}
-
-
}
状态模式(State)
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
适用性
一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常, 有多个操作包含这一相同的条件结构。S t a t e模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。
让我们回过头来看看策略模式:
单从UML图看来,它们之间似乎没有什么不一样……
再看看它的意图:
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
注意到了吗?
策略模式的目的是可以在运行时改变行为,让设计更有弹性;
而在状态模式中,“改变行为”这件事则是建立在它的方案中的,状态在状态对象集合中游走改变,它的对象行为也会跟着改变,可是使用对象的client却可以完全不了解它。
最后用一段来自《Head First 设计模式》的话:
一般来说,我们把策略模式想成是除了继承之外的一种弹性替代方案。如果你使用继承定义了一个类的行为,你将被这个行为困住,甚至要修改它都很难。有了策略模式,你可以通过组合不同的对象来改变行为。
我们把状态模式想成是不用在context中放置许多条件判断的替代方案。通过将行为包装进状态对象中,你可以通过在context内简单地改变状态对象来改变context的行为。