状态模式

定义

状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

这个模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象,行为会随着内部状态而改变。

在这里插入图片描述

场景描述

糖果机就跟街上那个卡一元钱后,小苹果能转出来小球很类似,对于糖果机来说,一共有四种状态,分别是没有25分钱的状态,有25分钱状态,有糖果状态,没糖果状态,其中1和2、3和4状态是互斥的。

图片

没引入设计模式之前的代码一般这样写:

public class GumballMachine {
   final static int SOLD_OUT = 0;
   final static int NO_QUARTER = 1;
   final static int HAS_QUARTER = 2;
   final static int SOLD = 3;
   int state = SOLD_OUT;
   int count = 0;

   public GumballMachine(int count) {
      this.count = count;
      if (count > 0) {
         state = NO_QUARTER;
      }
   }
   // 当有25分钱投进来,就会执行这里
   public void insertQuarter() {
      if (state == HAS_QUARTER) {
         System.out.println("You can't insert another quarter");
      } else if (state == NO_QUARTER) {
         state = HAS_QUARTER;
         System.out.println("You inserted a quarter");
      } else if (state == SOLD_OUT) {
         System.out.println("You can't insert a quarter, the machine is sold out");
      } else if (state == SOLD) {
         System.out.println("Please wait, we're already giving you a gumball");
      }
   }

   // 现在,如果顾客试着退回25分钱
   public void ejectQuarter() {
      if (state == HAS_QUARTER) {
         System.out.println("Quarter returned");
         state = NO_QUARTER;
      } else if (state == NO_QUARTER) {
         System.out.println("You haven't inserted a quarter");
      } else if (state == SOLD) {
         System.out.println("Sorry, you already turned the crank");
      } else if (state == SOLD_OUT) {
         System.out.println("You can't eject, you haven't inserted a quarter yet");
      }
   }

   // 顾客试着转动曲柄
   public void turnCrank() {
      if (state == SOLD) {
         System.out.println("Turning twice doesn't get you another gumball!");
      } else if (state == NO_QUARTER) {
         System.out.println("You turned but there's no quarter");
      } else if (state == SOLD_OUT) {
         System.out.println("You turned, but there are no gumballs");
      } else if (state == HAS_QUARTER) {
         System.out.println("You turned...");
         state = SOLD;
         dispense();
      }
   }

   // 调用此方法,发放糖果
   private void dispense() {
      if (state == SOLD) {
         System.out.println("A gumball comes rolling out the slot");
         count = count - 1;
         if (count == 0) {
            System.out.println("Oops, out of gumballs!");
            state = SOLD_OUT;
         } else {
            state = NO_QUARTER;
         }
      } else if (state == NO_QUARTER) {
         System.out.println("You need to pay first");
      } else if (state == SOLD_OUT) {
         System.out.println("No gumball dispensed");
      } else if (state == HAS_QUARTER) {
         System.out.println("No gumball dispensed");
      }
   }

   // 添加糖豆
   public void refill(int numGumBalls) {
      this.count = numGumBalls;
      state = NO_QUARTER;
   }

   public String toString() {
      StringBuffer result = new StringBuffer();
      result.append("Mighty Gumball, Inc.");
      result.append("Java-enabled Standing Gumball Model");
      result.append("Inventory: " + count + " gumball");
      if (count != 1) {
         result.append("s");
      }
      result.append("\nMachine is ");
      if (state == SOLD_OUT) {
         result.append("sold out");
      } else if (state == NO_QUARTER) {
         result.append("waiting for quarter");
      } else if (state == HAS_QUARTER) {
         result.append("waiting for turn of crank");
      } else if (state == SOLD) {
         result.append("delivering a gumball");
      }
      result.append("\n");
      return result.toString();
   }
}

变化

客户要求当曲柄转动时,有10%的几率掉下来的是两颗糖果(多送你一颗),对着上面的代码不妨停下来想一想。
按照Head First的剧情是增加一个新的状态,赢家状态。之后在上面的每个方法里面加上赢家的判断,更糟糕的是转动曲柄方法,因为首先要检查这个顾客是不是赢家。
发放糖果的时候假如剩最后一颗糖果,是不是就不能随机赢家了?
这种改法明显改的太多,不利于维护。

正解

新的代码,其中的精髓我觉的就在于,所有的状态都继承相同的接口,这样状态相互切换,没有任何影响,也就是抽象成同类的事物,不同的执行内容。例如在HasQuarterState状态,调用turnCrank()方法,在方法体里就设置Context到WinnerState状态,接下来Context执行request【开篇的类图】操作时,就又调的是WinnerState状态了。

 public interface State
{
      void insertQuater();//插入25分钱
      void ejectQuater(); //退回25分钱
      void turnCrank();   //转动曲柄
      void dispense();    //弹出糖果
}

public class WinnerState implements State {
    GumballMachine gumballMachine;
 
    public WinnerState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
 
    public void insertQuarter() {
        System.out.println("Please wait, we're already giving you a Gumball");
    }
 
    public void ejectQuarter() {
        System.out.println("Please wait, we're already giving you a Gumball");
    }
 
    public void turnCrank() {
        System.out.println("Turning again doesn't get you another gumball!");
    }
 
    // 我们在这里释放出两颗糖果,让你和进入NoQuarterState或SoldOutState
    public void dispense() {
        gumballMachine.releaseBall();
        if (gumballMachine.getCount() == 0) {
            gumballMachine.setState(gumballMachine.getSoldOutState());
        } else {
            gumballMachine.releaseBall();
            System.out.println("YOU'RE A WINNER! You got two gumballs for your quarter");
            if (gumballMachine.getCount() > 0) {
                gumballMachine.setState(gumballMachine.getNoQuarterState());
            } else {
                System.out.println("Oops, out of gumballs!");
                gumballMachine.setState(gumballMachine.getSoldOutState());
            }
        }
    }
 
    public void refill() { }
    
    public String toString() {
        return "despensing two gumballs for your quarter, because YOU'RE A WINNER!";
    }
}
public class HasQuarterState implements State {
    // 首先,我们增加一个随机数产生器,产生10%赢的机会
    Random randomWinner = new Random(System.currentTimeMillis());
    GumballMachine gumballMachine;
 
    public HasQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
  
    public void insertQuarter() {
        System.out.println("You can't insert another quarter");
    }
 
    public void ejectQuarter() {
        System.out.println("Quarter returned");
        gumballMachine.setState(gumballMachine.getNoQuarterState());
    }
 
    // 然后决定这个顾客是否赢了
    public void turnCrank() {
        System.out.println("You turned...");
        int winner = randomWinner.nextInt(10);
        if ((winner == 0) && (gumballMachine.getCount() > 1)) {
            gumballMachine.setState(gumballMachine.getWinnerState());
        } else {
            gumballMachine.setState(gumballMachine.getSoldState());
        }
    }

    public void dispense() {
        System.out.println("No gumball dispensed");
    }
    
}
public class GumballMachineTestDrive {

    public static void main(String[] args) {
        GumballMachine gumballMachine = 
            new GumballMachine(10);

        System.out.println(gumballMachine);

        gumballMachine.insertQuarter();
        gumballMachine.turnCrank();
        gumballMachine.insertQuarter();
        gumballMachine.turnCrank();

        System.out.println(gumballMachine);
        // 多重复几次,就可以看是否中奖了
    }
}
public class GumballMachine {  
    //状态实例  
    State soldOutState;  
    State noQuarterState;  
    State hasQuarterState;  
    State soldState;  
    State winnerState;  
      
    // 实例变量state,初始化为糖果售罄状态  
    State state = soldOutState;   
    // 记录机器内装有糖果的数目,开始机器是没有装糖果的  
    int count=0;  
    // 构造器取得糖果的初始数目并把它放在一个实例变量count中  
    public GumballMachine(int numberGumballs) {  
        // 每种状态都创建一个状态实例  
        soldOutState=new SoldOutState(this);  
        noQuarterState=new NoQuarterState(this);  
        hasQuarterState=new HasQuarterState(this);  
        soldState=new SoldState(this);  
        winnerState = new WinnerState(this);  
          
        this.count = numberGumballs;  
        // 若超过0颗糖果,就将状态设置为NoQuarterState  
        if(numberGumballs > 0) {  
            state = noQuarterState;  
        }  
    }  
    // 取得机器内的糖果数目  
    public int getCount() {  
        return count;  
    }  
    // 取得糖果售罄状态  
   // ……

    // 投入25分钱  
    public void insertQuarter(){  
        state.insertQuarter();  
    }  
    // 拒绝25分钱  
    public void ejectQuarter(){  
        state.ejectQuarter();  
    }  
    // 转动曲柄  
    public void turnCrank(){  
        state.turnCrank();  
        state.dispense();  
    }  
    // 设置状态  
    public void setState(State state){  
        this.state=state;  
    }  
    // 糖果滚出来一个  
    public void releaseBall(){  
        System.out.println("A gumball comes rolling out of the solt...");  
        if(count!=0){  
            count--;  
        }  
    }  
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值