老僧长谈设计模式-6-状态模式

声明:

本节内容主要来自Head First

好吧,现在带你进入策略模式的地盘

【what】

状态模式是什么?

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

封装基于状态的行为,并将行为委托到当前状态。

模式图:


【How】

聪明的人都会讲故事,源自于Head First 的糖果机故事华丽登场啦

从前有一家糖果公司,发现只要给糖果机装上芯片就更能赚钱,上状态图

聪明的程序员写出了糖果机1.0版,是这个样子:

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


万能糖果公司已经将代码放到新的机器当中,到目前为止,一切看来换算顺利;

然而,该来的躲不掉,程序员的噩耗->> 变更需求来啦!

糖果公司CEO决定加入一个买十次赢一次的游戏,10%的机会糖果机将会掉下两颗糖。

事情变得复杂了,看样子每个方法都得增加判断赢家状态的逻辑,这可有的忙了!

重构的时间到了,遵守“封装变化”的原则

①首先,定义一个State接口,在这个接口内,糖果机的每个动作都有一个对应的方法。

②然后为机器的每个状态实现状态类。这些类将负责在对应的状态下进行机器的行为。

③最后,我们要摆脱旧的条件代码,取而代之的方式是,将动作委托到状态类。


定义状态接口和类:

接口:

/* 
 * 状态接口State 
 */  
public interface State {  
   public void insertQuarter();   // 投入25分钱  
   public void ejectQuarter();    // 拒绝25分钱  
   public void turnCrank();       // 转动曲柄  
   public void dispense();        // 发放糖果  
} 


没有25分

/* 
 * 没有25分钱状态,实现了State接口 
 */  
  
public class NoQuarterState implements State{  
    GumballMachine gumballMachine;  
      
    public NoQuarterState(GumballMachine gumballMachine){  
        this.gumballMachine=gumballMachine;  
    }  
    // 投入25分钱  
    public void insertQuarter() {  
        System.out.println("You insert a quarter");  
        gumballMachine.setState(gumballMachine.getHasQuarterState());  
    }  
    // 拒绝25分钱  
    public void ejectQuarter() {  
        System.out.println("You haven't insert a quarter");  
    }  
    // 转动曲柄  
    public void turnCrank() {  
        System.out.println("You turned crank,but you there's no quarter");  
    }  
    // 发放糖果  
    public void dispense() {  
        System.out.println("You need to pay first");  
    }  
}  


有25分

/* 
 * 有25分钱状态,实现了State接口 
 */  
public class HasQuarterState implements State {  
    Random randomWinner = new Random(System.currentTimeMillis()); //首先我们增加一个随机数产生器,产生10%赢的机  
  
会  
    GumballMachine gumballMachine;  
      
    public HasQuarterState(GumballMachine gumballMachine){  
        this.gumballMachine = gumballMachine;  
    }  
    // 投入25分钱  
    public void insertQuarter() {  
        System.out.println("You can not insert anther quarter");  
    }  
    // 拒绝25分钱  
    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);  
        System.out.println("winner =" + winner);  
        if((winner ==0) && (gumballMachine.getCount() > 1)) {  
            gumballMachine.setState(gumballMachine.getWinnerState());  
        } else {  
            gumballMachine.setState(gumballMachine.getSoldState());  
        }  
    }  
    // 发放糖果  
    public void dispense() {  
        System.out.println("No gumball dispensed");  
    }  
}

售出糖果状态

/* 
 * 售出糖果状态,实现了State接口 
 */  
public class SoldState implements State{  
      
    GumballMachine gumballMachine;  
    public SoldState(GumballMachine gumballMachine) {  
        this.gumballMachine = gumballMachine;  
    }  
    // 投入25分钱  
    public void insertQuarter() {  
        System.out.println("Please wait, we're already giving you a gumball");  
    }  
    // 拒绝25分钱  
    public void ejectQuarter() {  
        System.out.println("Sorry,you have already turn crank");  
    }  
    // 转动曲柄  
    public void turnCrank() {  
        System.out.println("trun twice ,doesn't give you anthor gamball!");  
    }  
    // 发放糖果  
    public void dispense() {  
        gumballMachine.releaseBall();  
            if(gumballMachine.getCount()>0){  
                gumballMachine.setState(gumballMachine.getNoQuarterState());  
            } else {  
            System.out.println("Opps,out of gamball!");   
            gumballMachine.setState(gumballMachine.getSoldOutState());  
        }  
    }  
}

糖果售罄状态

/* 
 * 通过售罄状态,实现了State接口 
 */  
public class SoldOutState implements State{  
      
    GumballMachine gumballMachine;  
    public SoldOutState(GumballMachine gumballMachine){  
        this.gumballMachine=gumballMachine;  
    }  
    // 投入25分钱  
    public void insertQuarter() {  
        System.out.println("You can't insert a quarter, the machine is sold out");  
    }  
    // 拒绝25分钱  
    public void ejectQuarter() {  
        // TODO Auto-generated method stub  
        System.out.println("You can't eject, you haven't inserted a quarter yet");  
    }  
    // 转动曲柄  
    public void turnCrank() {  
        // TODO Auto-generated method stub  
        System.out.println("You turned, but there are no gumballs");  
    }  
    // 发放糖果  
    public void dispense() {  
        // TODO Auto-generated method stub  
        System.out.println("No gumball dispensed");  
    }  
} 

赢家状态

/* 
 * 赢家状态,实现了State接口 
 */  
public class WinnerState implements State{  
    GumballMachine gumballMachine;  
      
    public WinnerState(GumballMachine gumballMachine){  
        this.gumballMachine = gumballMachine;  
    }  
    // 投入25分钱  
    @Override  
    public void insertQuarter() {  
        // TODO Auto-generated method stub  
        System.out.println("Please wait, we're already giving you a gumball");  
    }  
    // 拒绝25分钱  
    @Override  
    public void ejectQuarter() {  
        // TODO Auto-generated method stub  
        System.out.println("Sorry,you have already turn crank");  
    }  
    // 转动曲柄  
    @Override  
    public void turnCrank() {  
        // TODO Auto-generated method stub  
        System.out.println("trun twice ,doesn't give you anthor gamball!");  
    }  
    // 发放糖果  
    @Override  
    public void dispense() {  
        // TODO Auto-generated method stub  
        System.out.println("You're a Winner! You get two gumballs for your quarter");  
        gumballMachine.releaseBall();  
        if(gumballMachine.getCount() == 0) {  
            gumballMachine.setState(gumballMachine.getSoldOutState());  
        } else {  
            gumballMachine.releaseBall();  
            if(gumballMachine.getCount()>0){  
                gumballMachine.setState(gumballMachine.getNoQuarterState());  
            } else {  
                System.out.println("Opps,out of gamball!");   
               gumballMachine.setState(gumballMachine.getSoldOutState());  
           }  
        }  
    }  
} 

重新改造糖果机Context

//糖果机上下文环境类Java源文件 GumballMachine.java  
package State;  
import State.HasQuarterState;  
import State.NoQuarterState;  
import State.SoldOutState;  
import State.SoldState;  
import State.WinnerState;  
import State.State;  
/* 
 *  糖果机器上下文环境接口类 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;  
    }  
    // 取得糖果售罄状态  
    public State getSoldOutState() {  
        return soldOutState;  
    }  
    // 取得没有25分钱状态  
    public State getNoQuarterState() {  
        return noQuarterState;  
    }  
    // 取得拥有25分钱  
    public State getHasQuarterState() {  
        return hasQuarterState;  
    }  
    // 取得售出糖果状态  
    public State getSoldState() {  
        return soldState;  
    }  
    // 取得赢家状态  
    public State getWinnerState() {  
        return winnerState;  
    }  
    // 投入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--;  
        }  
    }  
} 


测试类

//测试糖果机的Java源文件 GumballMachineTestDrive.java  
package State;  
/* 
 * 糖果机测试驱动程序:GumballMachineTestDrive.java 
 */  
public class GumballMachineTestDrive {  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        GumballMachine gumballMachine = new GumballMachine(5);  
          
        System.out.println(gumballMachine);  
        System.out.println("The current gumball number is:" + gumballMachine.getCount());  
        System.out.println("****************************************");  
          
          
        gumballMachine.insertQuarter();  
        gumballMachine.turnCrank();  
          
        System.out.println(gumballMachine);  
        System.out.println("The current gumball number is:" + gumballMachine.getCount());  
        System.out.println("****************************************");  
          
        gumballMachine.insertQuarter();  
        gumballMachine.turnCrank();  
        System.out.println(gumballMachine);   
        System.out.println("The current gumball number is:" + gumballMachine.getCount());  
        System.out.println("****************************************");  
          
        gumballMachine.insertQuarter();  
        gumballMachine.turnCrank();  
        System.out.println(gumballMachine);   
        System.out.println("The current gumball number is:" + gumballMachine.getCount());  
        System.out.println("****************************************");  
    }  
  
} 


【why】

为什么需要状态模式?

【where & when】

应用的场合?时间?

状态模式 和 策略模式 区别:

两个模式的类图是很相似的

以状态模式而言,我们将一群行为封装在状态对象中,context的行为随时可委托到那些状态对象中的一个。随着时间的流逝,当前状态在状态对象集合中游走改变,以反映出context内部的状态,因此,context的行为也跟着改变。但context客户对于状态了解不多,甚至浑然不觉。

而以策略模式而言,客户通常主动指定context所要组合的策略对象是哪一个。策略模式让我们具有弹性,能够在运行时改变策略,但对某个context对象来说,通常只有一个最适合的策略对象。

一般来说,我们把策略模式想成是除了继承之外的一种弹性替代方案。如果你使用继承定义了一个类的行为,你将被这个类困住,甚至想要修改都很难,有了策略模式,你可以通过组合不同的对象来改变行为。

我们把状态模式想成是不用在context中放置很多条件判断的替代方案,通过将行为包装在状态对象中,你可以通过在context内简单地改变状态对象来改变context的行为。

【总结】

糖果机的故事太TM牛了~~

最后,猴子来镇楼~~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值