Liny_@NotePad

沉迷ACG中

糖果机的状态转换。。

YOYO posted @ 2009年4月18日 05:00 in 【OOA/D】 with tags Strategy State 模式 , 2596 阅读

一台糖果机,支持n种操作,譬如:投入硬币、退还硬币、转动曲柄、填充糖果……
而不同状态下不同操作返回的内容不一样。下面是它的状态转换图:

如何实现它的转换呢?
这是一个不使用模式的例子:

  1. package state.old;
  2.  
  3. public class OldCandyMachine {
  4.        
  5.         private int state;
  6.         public static int EMPTY_WORK_STATE = 0;
  7.         public static int FREE_WORK_STATE = 1;
  8.         public static int HAVE_MONEY_WORK_STATE = 2;
  9.        
  10.         private int candyCount;
  11.  
  12.         public OldCandyMachine(int candyCount){
  13.                 this.candyCount = candyCount;
  14.                 if(candyCount>0){
  15.                         state = OldCandyMachine.FREE_WORK_STATE;
  16.                 }else{
  17.                         state = OldCandyMachine.EMPTY_WORK_STATE;
  18.                 }
  19.         }
  20.        
  21.         public int getCandyCount() {
  22.                 return candyCount;
  23.         }
  24.        
  25.         public void setCandyCount(int candyCount){
  26.                 this.candyCount = candyCount;
  27.         }
  28.        
  29.         public boolean fillCandy(int candyCount){
  30.                 if(state == OldCandyMachine.EMPTY_WORK_STATE){
  31.                         this.candyCount = candyCount;
  32.                         this.state = OldCandyMachine.FREE_WORK_STATE;
  33.                         return true;
  34.                 }
  35.                 return false;
  36.         }
  37.        
  38.         public boolean inputMoney(){
  39.                 if( state == OldCandyMachine.FREE_WORK_STATE ){
  40.                         state = OldCandyMachine.HAVE_MONEY_WORK_STATE;
  41.                         return true;
  42.                 }
  43.                 if( state == OldCandyMachine.EMPTY_WORK_STATE ){
  44.                         System.out.println("糖果机里没有糖果!");
  45.                         return false;
  46.                 }
  47.                 if( state == OldCandyMachine.HAVE_MONEY_WORK_STATE ){
  48.                         System.out.println("糖果机里已经有钱了!");
  49.                         return false;
  50.                 }
  51.                 return false;
  52.         }
  53.        
  54.         public boolean returnMoney(){
  55.                 if( state == OldCandyMachine.HAVE_MONEY_WORK_STATE ){
  56.                         state = OldCandyMachine.FREE_WORK_STATE;
  57.                         return true;
  58.                 }
  59.                 return false;
  60.         }
  61.        
  62.         public boolean turn(){
  63.                 if( state == OldCandyMachine.HAVE_MONEY_WORK_STATE ){
  64.                         state = OldCandyMachine.FREE_WORK_STATE;                       
  65.                         outCandy();
  66.                         return true;
  67.                 }
  68.                 return false;
  69.         }
  70.        
  71.         public void outCandy(){
  72.                 System.out.println("糖果机弹出了糖果!");
  73.                 candyCount--;
  74.                 if(candyCount==0){
  75.                         state = OldCandyMachine.EMPTY_WORK_STATE;
  76.                 }
  77.         }
  78.  
  79. }

 

在这样的代码下,如果我们要增加一个状态,该修改多少代码?
它的条件语句过多,容易出错,状态转换并不明显,
下面,我们尝试着用状态模式来重构。

[CandyMachine]使用了状态模式的、崭新的糖果机

  1. package state;
  2.  
  3. import state.workstate.EmptyWorkState;
  4. import state.workstate.FreeWorkState;
  5. import state.workstate.WorkState;
  6.  
  7.  
  8. public class CandyMachine {
  9.        
  10.         private WorkState workState;
  11.        
  12.         private int candyCount;
  13.  
  14.         public CandyMachine(int candyCount){
  15.                 this.candyCount = candyCount;
  16.                 if(candyCount>0){
  17.                         workState = new FreeWorkState(this);
  18.                 }else{
  19.                         workState = new EmptyWorkState(this);
  20.                 }
  21.         }
  22.        
  23.         public int getCandyCount() {
  24.                 return candyCount;
  25.         }
  26.        
  27.         public void setCandyCount(int candyCount){
  28.                 this.candyCount = candyCount;
  29.         }
  30.  
  31.         public WorkState getWorkState() {
  32.                 return workState;
  33.         }
  34.  
  35.         public void setWorkState(WorkState workState) {
  36.                 this.workState = workState;
  37.         }
  38.        
  39.         public boolean fillCandy(int candyCount){
  40.                 if(workState.fillCandy()){
  41.                         this.candyCount = candyCount;
  42.                         return true;
  43.                 }
  44.                 return false;
  45.         }
  46.        
  47.         public boolean inputMoney(){
  48.                 return workState.inputMoney();
  49.         }
  50.        
  51.         public boolean returnMoney(){
  52.                 return workState.returnMoney();
  53.         }
  54.        
  55.         public boolean turn(){
  56.                 if(workState.turn()){
  57.                         outCandy();
  58.                         return true;
  59.                 }
  60.                 return false;
  61.         }
  62.        
  63.         public void outCandy(){
  64.                 System.out.println("糖果机弹出了糖果!");
  65.                 candyCount--;
  66.                 if(candyCount==0){
  67.                         this.setWorkState(new EmptyWorkState(this));
  68.                 }
  69.         }
  70.        
  71. }

 

[WorkState]糖果机工作状态:抽象类,也可定义成接口,在这里约定要实现的方法(糖果机能进行的操作)。

  1. package state.workstate;
  2.  
  3. import state.CandyMachine;
  4.  
  5.  
  6. public abstract class WorkState {
  7.        
  8.         public CandyMachine machine;
  9.        
  10.         public WorkState(CandyMachine machine){
  11.                 this.machine = machine;
  12.         }
  13.        
  14.         public abstract boolean fillCandy();
  15.        
  16.         public abstract boolean inputMoney();
  17.        
  18.         public abstract boolean returnMoney();
  19.        
  20.         public abstract boolean turn();
  21.  
  22. }

 

[EmptyWorkState]糖果售罄的糖果机工作状态

  1. package state.workstate;
  2.  
  3. import state.CandyMachine;
  4.  
  5.  
  6. public class EmptyWorkState extends WorkState {
  7.  
  8.         public EmptyWorkState(CandyMachine machine) {
  9.                 super(machine);
  10.         }
  11.  
  12.         @Override
  13.         public boolean fillCandy() {
  14.                 System.out.println("填满糖果机!");
  15.                 machine.setWorkState(new FreeWorkState(machine));
  16.                 return true;
  17.         }
  18.  
  19.         @Override
  20.         public boolean inputMoney() {
  21.                 System.out.println("糖果机里没有糖果!");
  22.                 return false;
  23.         }
  24.  
  25.         @Override
  26.         public boolean returnMoney() {
  27.                 System.out.println("糖果机里没有钱!");
  28.                 return false;
  29.         }
  30.  
  31.         @Override
  32.         public boolean turn() {
  33.                 System.out.println("糖果机里没有糖果!");
  34.                 return false;
  35.         }
  36.  
  37. }

 

[FreeWorkState]空闲的糖果机工作状态

  1. package state.workstate;
  2.  
  3. import state.CandyMachine;
  4.  
  5.  
  6. public class FreeWorkState extends WorkState {
  7.  
  8.         public FreeWorkState(CandyMachine machine) {
  9.                 super(machine);
  10.         }
  11.  
  12.         @Override
  13.         public boolean fillCandy() {
  14.                 System.out.println("糖果机已满!");
  15.                 return false;
  16.         }
  17.  
  18.         @Override
  19.         public boolean inputMoney() {
  20.                 System.out.println("钱已投入糖果机!");
  21.                 machine.setWorkState(new HaveMoneyWorkState(machine));
  22.                 return true;
  23.         }
  24.  
  25.         @Override
  26.         public boolean returnMoney() {
  27.                 System.out.println("糖果机中没有钱!");
  28.                 return false;
  29.         }
  30.  
  31.         @Override
  32.         public boolean turn() {
  33.                 System.out.println("还没有投币不能使用!");
  34.                 return false;
  35.         }
  36.  
  37. }

 

[HaveMoneyWorkState]已填充硬币后的糖果机工作状态

  1. package state.workstate;
  2.  
  3. import state.CandyMachine;
  4.  
  5.  
  6. public class HaveMoneyWorkState extends WorkState {
  7.  
  8.         public HaveMoneyWorkState(CandyMachine machine) {
  9.                 super(machine);
  10.         }
  11.  
  12.         @Override
  13.         public boolean fillCandy() {
  14.                 System.out.println("糖果机已满!");
  15.                 return false;
  16.         }
  17.  
  18.         @Override
  19.         public boolean inputMoney() {
  20.                 System.out.println("糖果机已有钱!");
  21.                 return false;
  22.         }
  23.  
  24.         @Override
  25.         public boolean returnMoney() {
  26.                 System.out.println("糖果机已退钱!");
  27.                 machine.setWorkState(new FreeWorkState(machine));
  28.                 return true;
  29.         }
  30.  
  31.         @Override
  32.         public boolean turn() {
  33.                 System.out.println("使用糖果机!");
  34.                 machine.setWorkState(new FreeWorkState(machine));
  35.                 return true;
  36.         }
  37.  
  38. }

以及它的UT

  1. package state;
  2.  
  3. import state.old.OldCandyMachine;
  4. import state.workstate.*;
  5. import junit.framework.TestCase;
  6.  
  7. public class TestCandyMachineState extends TestCase {
  8.        
  9.         public void testOldCandyMachine(){
  10.                 OldCandyMachine machine = new OldCandyMachine(0);
  11.                
  12.                 assertFalse(machine.inputMoney());
  13.                 assertFalse(machine.returnMoney());
  14.                 assertFalse(machine.turn());
  15.                 assertTrue(machine.fillCandy(3));       //     此处对糖果机填充:状态已经改变
  16.                
  17.                 assertEquals(machine.getCandyCount(),3);        // 填充三个
  18.                
  19.                 assertFalse(machine.returnMoney());
  20.                 assertFalse(machine.fillCandy(3));
  21.                 assertFalse(machine.turn());
  22.                 assertTrue(machine.inputMoney());       //     此处将钱放入糖果机:状态已经改变
  23.                
  24.                 assertFalse(machine.inputMoney());
  25.                 assertFalse(machine.fillCandy(3));
  26.                 assertTrue(machine.returnMoney());      //    此处从糖果机退钱:状态已经改变
  27.                
  28.                 assertFalse(machine.returnMoney());
  29.                 assertFalse(machine.fillCandy(3));
  30.                 assertFalse(machine.turn());
  31.                 assertTrue(machine.inputMoney());       //     此处将钱放入糖果机:状态已经改变
  32.                
  33.                 assertFalse(machine.inputMoney());
  34.                 assertFalse(machine.fillCandy(3));
  35.                 assertTrue(machine.turn());               //   此处使用糖果机:状态已经改变
  36.                
  37.                 assertEquals(machine.getCandyCount(),2);        //      使用过一次后数量-1
  38.         }
  39.        
  40.         public void testCandyMachine(){
  41.                 CandyMachine machine = new CandyMachine(0);
  42.                
  43.                 assertFalse(machine.inputMoney());
  44.                 assertFalse(machine.returnMoney());
  45.                 assertFalse(machine.turn());
  46.                 assertTrue(machine.getWorkState() instanceof EmptyWorkState );
  47.                 assertTrue(machine.fillCandy(3));       //     此处对糖果机填充:状态已经改变
  48.                 assertTrue(machine.getWorkState() instanceof FreeWorkState );
  49.                
  50.                 assertEquals(machine.getCandyCount(),3);        // 填充三个
  51.                
  52.                 assertFalse(machine.returnMoney());
  53.                 assertFalse(machine.fillCandy(3));
  54.                 assertFalse(machine.turn());
  55.                 assertTrue(machine.getWorkState() instanceof FreeWorkState );
  56.                 assertTrue(machine.inputMoney());       //     此处将钱放入糖果机:状态已经改变
  57.                 assertTrue(machine.getWorkState() instanceof HaveMoneyWorkState );
  58.                
  59.                 assertFalse(machine.inputMoney());
  60.                 assertFalse(machine.fillCandy(3));
  61.                 assertTrue(machine.getWorkState() instanceof HaveMoneyWorkState );
  62.                 assertTrue(machine.returnMoney());      //    此处从糖果机退钱:状态已经改变
  63.                 assertTrue(machine.getWorkState() instanceof FreeWorkState );
  64.                
  65.                 assertFalse(machine.returnMoney());
  66.                 assertFalse(machine.fillCandy(3));
  67.                 assertFalse(machine.turn());
  68.                 assertTrue(machine.getWorkState() instanceof FreeWorkState );
  69.                 assertTrue(machine.inputMoney());       //     此处将钱放入糖果机:状态已经改变
  70.                 assertTrue(machine.getWorkState() instanceof HaveMoneyWorkState );
  71.                
  72.                 assertFalse(machine.inputMoney());
  73.                 assertFalse(machine.fillCandy(3));
  74.                 assertTrue(machine.getWorkState() instanceof HaveMoneyWorkState );
  75.                 assertTrue(machine.turn());               //   此处使用糖果机:状态已经改变
  76.                 assertTrue(machine.getWorkState() instanceof FreeWorkState );
  77.                
  78.                 assertEquals(machine.getCandyCount(),2);        //      使用过一次后数量-1
  79.         }
  80.  
  81. }

 

状态模式(State)


允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

适用性
一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常, 有多个操作包含这一相同的条件结构。S t a t e模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

让我们回过头来看看策略模式

单从UML图看来,它们之间似乎没有什么不一样……

再看看它的意图:

定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

注意到了吗?
策略模式的目的是可以在运行时改变行为,让设计更有弹性;
而在状态模式中,“改变行为”这件事则是建立在它的方案中的,状态在状态对象集合中游走改变,它的对象行为也会跟着改变,可是使用对象的client却可以完全不了解它。

最后用一段来自《Head First 设计模式》的话:

  一般来说,我们把策略模式想成是除了继承之外的一种弹性替代方案。如果你使用继承定义了一个类的行为,你将被这个行为困住,甚至要修改它都很难。有了策略模式,你可以通过组合不同的对象来改变行为。
我们把状态模式想成是不用在context中放置许多条件判断的替代方案。通过将行为包装进状态对象中,你可以通过在context内简单地改变状态对象来改变context的行为。

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter