状态模式

状态模式,允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。(通过这个模式将状态封装成独立的类,并将动作委托到代表当前状态的对象)

举个栗子:

有个糖果机控制器,有四种状态:东西售完(SOLD_OUT)、还未投25分钱币(NO_QUATER)、已投25分钱币(HAS_QUATER)、卖出东西(SOLD)。

执行投入25分钱硬币:

(1)糖果机的状态是已投入25分钱:提示不能重复投入25分钱硬币。

(2)糖果机的状态是未投入25分钱:状态改为已投入。

(3)糖果机的状态是已售罄:提示不能投入25分钱硬币,因为已售罄。

(4)糖果机的状态是已卖出:提示等待,刚刚发放糖果。

同理:还有其他操作,比如退回25分钱,转动曲柄操作等等,都会经历上述的几种状态。

不推荐的代码:

将所有的状态逻辑同条件语句混杂在一起的代码:

GumballMachine.java 类:

public class GumballMachine
{
    final static int SOLD_OUT = 0;
    final static int NO_QUATER = 1;
    final static int HAS_QUATER = 2;
    final static int SOLD = 3;

    // 一开始被设置为“糖果售罄”, state用于追踪机器内的糖果数目
    int state = SOLD_OUT;
    int count = 0;

    public GumballMachine(int count){
        this.count = count;
        if (count > 0){
            // 如果库存不为0的话,机器会进入“没有25分钱”的状态,也就是说它等着别人投入25分钱,
            // 如果糖果数目为0的话,机器会保持在“糖果售罄”的状态
            state = NO_QUATER;
        }
    }

    /**
     *  投入25分钱,会执行这里
     */
    public void insertQuarter(){
        if (state == HAS_QUATER){ // 已投入25分钱
            System.out.println("You can't insert another quater");
        } else if ( state == NO_QUATER ){
            state = HAS_QUATER;
            System.out.println("You inserted a quater");
        } else if (state == SOLD_OUT ){
            System.out.println("You can't insert a quater the machine is sold out");
        } else if (state == SOLD ){ // 如果顾客刚刚买了糖果,就需要稍等一下,好让状态转换完毕,恢复到“没有25分钱”状态
            System.out.println("Please wait, we're already giving you a gumball");
        }
    }

    /**
     * 退回25分钱的操作
     */
    public void ejectQuater() {
        if (state == HAS_QUATER){
            System.out.println("Quater returned");
            state = NO_QUATER;
        } else if ( state == NO_QUATER ){
            System.out.println("You haven't inserted a quater");
        } else if ( state == SOLD ){ // 如果顾客已经转动曲柄,就不能再退钱了,他已经拿到糖果了
            System.out.println("Sorry, you already turned the crank");
        } else if ( state == SOLD_OUT ){ // 如果糖果售罄,就不能接受25分钱,当然也不能退钱
            System.out.println("You can't eject, you haven't inserted a quater yet");
        }
    }

    /**
     * 转动曲柄操作
     */
    public void turnCrank(){
        if ( state == SOLD ){ // 用户转动或曲柄,且拿到了糖果,再次转动曲柄时的提示
            System.out.println("Turning twice does't get you another gumball!");
        } else if ( state == NO_QUATER ){
            System.out.println("You turned but there's no quater");
        } else if ( state == SOLD_OUT ){
            System.out.println("You turned, but there are no gumballs");
        } else if ( state == HAS_QUATER ){
            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_QUATER;
            }
        } else if ( state == NO_QUATER ) { // 以下这些是不该发生的,但如果顾客这么做了,他们得到的是错误信息,而不是得到糖果
            System.out.println("You need to pay first");
        } else if ( state == SOLD_OUT ){
            System.out.println("No gumball dispensed");
        } else if ( state == HAS_QUATER ){
            System.out.println("No gumball dispensed");
        }
    }

    @Override
    public String toString() {
        // 公司相关信息
        String info = "";
        info += "Mighty Gumball, Inc. \n";
        info += "Java-enabled standing Gumball Model #2004";
        String inventory = "Inventory: " + this.count + " gumballs \n";
        info += inventory;
        if ( state == SOLD_OUT){
            info += "Machine is sold out";
        } else if (state == NO_QUATER){
            info += "Machine is waiting for quater";
        } else if (state == HAS_QUATER){
            info += "Machine has a quater";
        } else if (state == SOLD){
            info += "Machine has sold a gumball";
        }
        return info;
    }
}

上述代码的测试语句如下:

public static void main(String[] args) {
    GumballMachine gumballMachine = new GumballMachine(5); // 总共装了5颗糖果

    System.out.println(gumballMachine); // 打印出打印机的状态

    gumballMachine.insertQuarter(); // 投入一枚25分钱硬币
    gumballMachine.turnCrank(); // 转动曲柄,我们应该拿到糖果

    System.out.println(gumballMachine); // 再一次打印出机器的状态

    gumballMachine.insertQuarter(); // 投入一枚25分钱硬币
    gumballMachine.ejectQuater();  //  要求机器退钱
    gumballMachine.turnCrank(); // 转动曲柄,我们应该拿不到钱

    System.out.println(gumballMachine); // 再次打印出打印机的状态

    gumballMachine.insertQuarter(); // 投入一枚25分钱的硬币
    gumballMachine.turnCrank(); // 转动曲柄,我们应该拿到糖果
    gumballMachine.insertQuarter(); // 投入一枚25分钱硬币
    gumballMachine.turnCrank(); // 转动曲柄,我们应该拿到糖果
    gumballMachine.ejectQuater(); // 要求机器退钱

    System.out.println(gumballMachine);

    gumballMachine.insertQuarter();
    gumballMachine.insertQuarter(); // 放进两枚25分钱硬币
    gumballMachine.turnCrank();  // 转动曲柄,我们应该拿到糖果
    gumballMachine.insertQuarter(); // 做压力测试
    gumballMachine.turnCrank();
    gumballMachine.insertQuarter();
    gumballMachine.turnCrank();

    System.out.println(gumballMachine); // 再一次打印出机器的状态
}
输出结果为:
Mighty Gumball, Inc. 
Java-enabled standing Gumball Model #2004Inventory: 5 gumballs 
Machine is waiting for quater
You inserted a quater
You turned...
A gumball comes rolling out the slot
Mighty Gumball, Inc. 
Java-enabled standing Gumball Model #2004Inventory: 4 gumballs 
Machine is waiting for quater
You inserted a quater
Quater returned
You turned but there's no quater
Mighty Gumball, Inc. 
Java-enabled standing Gumball Model #2004Inventory: 4 gumballs 
Machine is waiting for quater
You inserted a quater
You turned...
A gumball comes rolling out the slot
You inserted a quater
You turned...
A gumball comes rolling out the slot
You haven't inserted a quater
Mighty Gumball, Inc. 
Java-enabled standing Gumball Model #2004Inventory: 2 gumballs 
Machine is waiting for quater
You inserted a quater
You can't insert another quater
You turned...
A gumball comes rolling out the slot
You inserted a quater
You turned...
A gumball comes rolling out the slot
Oops, out of gumballs
You can't insert a quater the machine is sold out
You turned, but there are no gumballs
Mighty Gumball, Inc. 
Java-enabled standing Gumball Model #2004Inventory: 0 gumballs 
Machine is sold out

分析:

上面的代码通过巨大的、整块的条件语句,让代码变的难以维护。

解决办法:

通过将不同状态封装在不同的对象中,可以让状态变得很干净,在以后理解和维护它们时,就可以省下很多功夫。

状态模式实现的代码如下:

创建一个State接口,所有的状态都必须实现这个接口

State.java 类如下:

public interface State {
    void insertQuater();
    void ejectQuater();
    void turnCrank();
    void dispense();
}

糖果机的几种类对应的状态如下:

SoldState.java 类:

public class SoldState implements State{

    GumballMachine gumballMachine;

    public SoldState(GumballMachine gumballMachine){
        this.gumballMachine = gumballMachine;
    }

    @Override
    public void insertQuater() {
        System.out.println("Please wait, we're already giving you a gumball");
    }

    @Override
    public void ejectQuater() {
        System.out.println("Sorry, you already turned the crank");
    }

    @Override
    public void turnCrank() {
        System.out.println("Turning twice doesn't get you another gumball");
    }

    @Override
    public void dispense() {
        gumballMachine.releaseBall();
        if (gumballMachine.getCount() > 0){
            gumballMachine.setState(gumballMachine.getNoQuaterState());
        } else {
            System.out.println("Oops, out of gumballs");
            gumballMachine.setState(gumballMachine.getSoldOutState());
        }
    }
}

SoldOutState.java 类:

public class SoldOutState implements State {

    GumballMachine gumballMachine;

    public SoldOutState(GumballMachine gumballMachine){
        this.gumballMachine = gumballMachine;
    }

    @Override
    public void insertQuater() {
        System.out.println("You can't insert a quater, the machine is sold out");
    }

    @Override
    public void ejectQuater() {
        System.out.println("You can't eject, you haven't inserted a quater yet");
    }

    @Override
    public void turnCrank() {
        System.out.println("You turned, but there are no gumballs");
    }

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

NoQuaterState.java 类:

public class NoQuaterState implements State {

    GumballMachine gumballMachine;

    public NoQuaterState(GumballMachine gumballMachine){
        this.gumballMachine = gumballMachine;
    }

    @Override
    public void insertQuater() {
        System.out.println("You inserted a quater");
        gumballMachine.setState(gumballMachine.getHasQuaterState());
    }

    @Override
    public void ejectQuater() {
        System.out.println("You haven't inserted a quater");
    }

    @Override
    public void turnCrank() {
        System.out.println("You turned, but there's no quater");
    }

    @Override
    public void dispense() {
        System.out.println("You need to pay first");
    }
}

HasQuaterState.java 类:

public class HasQuaterState implements State {

    Random randomWinner = new Random(System.currentTimeMillis());
    GumballMachine gumballMachine;

    public HasQuaterState(GumballMachine gumballMachine){
        this.gumballMachine = gumballMachine;
    }

    @Override
    public void insertQuater() { // 已经放入15分钱,不允许再放入15分钱
        System.out.println("You can't insert another quater");
    }

    @Override
    public void ejectQuater() { // 退钱操作,然后将机器的状态置为无15分钱的状态
        System.out.println("Quater returned");
        gumballMachine.setState(gumballMachine.getNoQuaterState());
    }

    @Override
    public void turnCrank() { // 曲柄转动时,转动出相应的东西,并将机器状态置为已经售出
        System.out.println("You turned...");
        int winner = randomWinner.nextInt(10);
        if((winner == 0) && (gumballMachine.getCount() > 1)){ // 这里因为winner的值是0到9中的任意一个我们取0,则是10%的概率问题
            gumballMachine.setState(gumballMachine.getWinnerState());
        }else{
            gumballMachine.setState(gumballMachine.getSoldState());
        }
    }

    @Override
    public void dispense() { // 此状态的另一个不恰当的动作
        System.out.println("No gumball dispensed");
    }
}

WinnerState.java 类如下:

public class WinnerState implements State{

    GumballMachine gumballMachine;

    public WinnerState(GumballMachine gumballMachine){
        this.gumballMachine = gumballMachine;
    }

    @Override
    public void insertQuater() {
        System.out.println("Please wait, we're already giving you a gumball");
    }

    @Override
    public void ejectQuater() {
        System.out.println("Sorry, you already turned the crank");
    }

    @Override
    public void turnCrank() {
        System.out.println("Turning twice doesn't get you another gumball");
    }

    @Override
    public void dispense() {
        System.out.println("YOU'RE A WINNER! you get two gumballs for you quater");
        gumballMachine.releaseBall();
        if(gumballMachine.getCount() == 0){
            gumballMachine.setState(gumballMachine.getSoldOutState()); //  如果还有第二颗糖果的话,我们就把它释放出来
        } else {
            gumballMachine.releaseBall();
            if (gumballMachine.getCount() > 0){
                gumballMachine.setState(gumballMachine.getNoQuaterState());
            } else {
                System.out.println("Oops, out of gumballs");
                gumballMachine.setState(gumballMachine.getSoldOutState());
            }
        }
    }
}

GumballMachine.java 类:

public class GumballMachine {

    State soldOutState;
    State noQuaterState;
    State hasQuaterState;
    State soldState;
    State winnerState;

    State state = soldOutState;
    int count = 0;

    public GumballMachine(int numberGumballs){
        soldOutState = new SoldOutState(this);
        noQuaterState = new NoQuaterState(this);
        hasQuaterState = new HasQuaterState(this);
        soldState = new SoldState(this);
        winnerState = new WinnerState(this);
        if (numberGumballs > 0){
            state = noQuaterState;
            this.count = numberGumballs;
        }
    }

    public void insertQuater(){
        state.insertQuater();
    }

    public void ejectQuater(){
        state.ejectQuater();
    }

    public void turnCrank(){
        state.turnCrank();
        state.dispense();
    }

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

    void releaseBall(){
        System.out.println("A gumball come rolling out the slot...");
        if ( count != 0 ){
            count = count - 1;
        }
    }

    public State getSoldOutState() {
        return soldOutState;
    }

    public void setSoldOutState(State soldOutState) {
        this.soldOutState = soldOutState;
    }

    public State getNoQuaterState() {
        return noQuaterState;
    }

    public void setNoQuaterState(State noQuaterState) {
        this.noQuaterState = noQuaterState;
    }

    public State getHasQuaterState() {
        return hasQuaterState;
    }

    public void setHasQuaterState(State hasQuaterState) {
        this.hasQuaterState = hasQuaterState;
    }

    public State getSoldState() {
        return soldState;
    }

    public void setSoldState(State soldState) {
        this.soldState = soldState;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public State getWinnerState() {
        return winnerState;
    }

    public void setWinnerState(State winnerState) {
        this.winnerState = winnerState;
    }

    @Override
    public String toString() {
        // 公司相关信息
        String info = "";
        info += "Mighty Gumball, Inc. \n";
        info += "Java-enabled standing Gumball Model #2004";
        String inventory = "Inventory: " + this.count + " gumballs \n";
        info += inventory;
        if ( state instanceof SoldOutState){
            info += "Machine is sold out";
        } else if (state instanceof NoQuaterState){
            info += "Machine is waiting for quater";
        } else if (state instanceof HasQuaterState){
            info += "Machine has a quater";
        } else if (state instanceof SoldState){
            info += "Machine has sold a gumball";
        }
        return info;
    }
}

编写一个测试类如下:

GumballMachineTestDrive.java 类:

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

    System.out.println(gumballMachine);

    gumballMachine.insertQuater();
    gumballMachine.turnCrank();

    System.out.println(gumballMachine);

    gumballMachine.insertQuater();
    gumballMachine.turnCrank();
    gumballMachine.insertQuater();
    gumballMachine.turnCrank();

    System.out.println(gumballMachine);
}

输出:
Mighty Gumball, Inc.

Java-enabled standing Gumball Model #2004Inventory: 5 gumballs

Machine is waiting for quater

You inserted a quater

You turned...

A gumball comes rolling out the slot

Mighty Gumball, Inc.

Java-enabled standing Gumball Model #2004Inventory: 4 gumballs

Machine is waiting for quater

You inserted a quater

Quater returned

You turned but there's no quater

Mighty Gumball, Inc.

Java-enabled standing Gumball Model #2004Inventory: 4 gumballs

Machine is waiting for quater

You inserted a quater

You turned...

A gumball comes rolling out the slot

You inserted a quater

You turned...

A gumball comes rolling out the slot

You haven't inserted a quater

Mighty Gumball, Inc.

Java-enabled standing Gumball Model #2004Inventory: 2 gumballs

Machine is waiting for quater

You inserted a quater

You can't insert another quater

You turned...

A gumball comes rolling out the slot

You inserted a quater

You turned...

A gumball comes rolling out the slot

Oops, out of gumballs

You can't insert a quater the machine is sold out

You turned, but there are no gumballs

Mighty Gumball, Inc.

Java-enabled standing Gumball Model #2004Inventory: 0 gumballs

Machine is sold out



分析:

上述代码通过将5种不同的状态:

SoldState.java 正在销售的状态;

SoldOutState.java 售罄的状态;

NoQuaterState.java 未投入25分钱的状态;

HasQuaterState.java 投入25分钱状态;以及后期新加入的状态WinnerState.java 可以售出两个糖果的状态;

所有的这些状态,都是寄生在一台糖果机GumballMachine.java 类上。

GumballMachine.java 和所有状态类是组合的关系,GumballMachine.java中通过字段包含了所有的状态,而各个状态类中字段有糖果机类的实例。

初始时,通过GumballMachine gumballMachine = new GumballMachine(5);将糖果机的糖果数量设置为5,而且如果糖果的数量大于0,则初始的状态是NoQuaterState,此时如果执行gumballMahcine的insert,即执行了NoQuaterState中的insert,则相应的状态会改变成HasQuaterState,此时,执行turnCrank则状态会变成soldState状态,然后调用相关的dispense方法。(注意:这里的state,全程只有一种状态,在不断的切换)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值