状态模式

来源:互联网 发布:阿里云投资了多少钱 编辑:程序博客网 时间:2024/06/10 21:23

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

举个栗子:

有个糖果机控制器,有四种状态:东西售完(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,全程只有一种状态,在不断的切换)

原创粉丝点击