设计模式之状态模式

状态模式,我们给他一个通俗的定义:在我们改变某一个对象的内部状态的时候,我们也要同时改变它的行为,表面上是改变了动作和行为,实际上则是改变了类的内部的状态。通过这个定义我们可以隐约的猜想到,我们在改变的状态的时候,其实是改变的一个类,也就是说一个类封装了一个状态,这个思想和策略模式比较像,将变化的封装起来,在策略模式中封装的是一个算法,以及算法的流程,其算法的大致过程一致,只是具体到某一个算法时会进行区分,可以看到这个组合的思想也能在状态模式中体现出来。类似于下面的表达:

  State:实际上定义了一个所有具体状态的共同接口,任何状态都是用这个接口,这样状态之间就可以互相替换。ConcreteA和ConcreteB两个处理来自Context的请求,每一个ContextState都提供了他自己对请求的实现

考虑下面一个例子:假如有一个糖果机,一个糖果的价格是25美分,糖果机默认状态是没有25美分的状态,当向糖果机中投入25美分后,我们就可以从糖果机的曲柄转动得到一个糖果,此时就出售了糖果,而当糖果机中没有糖果时,就会发出糖果售罄的状态,同时退换投入的25美分。我们发现在这个逻辑过程中,除了动作外,状态也是会发生改变的,所以,综合分析来看,我们有5个状态:投入25美分,退回25美分,转动曲柄,发放糖果,糖果售罄。分析后我们首先用类图设计,然后用代码来表示:

类图设计为:

实现为(完整代码地址为:GOGOGO):

/**
 * @author Tianlong Zhang
 * @time 2018/9/3 10:17
 * @project DesignPattern
 * @Version 1.0.0
 */
public class GumballMachine {

    //定义状态字段

    /*糖果售罄*/
    final static int SOLD_OUT = 0;

    /*没有投入25美分,默认初始状态*/
    final static int NO_QUARTER = 1;

    /*投入25美分*/
    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 insert 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 refill(int numGumballs){
        this.count = numGumballs;
        state = NO_QUARTER;
    }

    public String toString(){
        StringBuffer result = new StringBuffer();
        result.append("\nMighty Gumball, Inc.");
        result.append("\nJava-enabled Standing Gumball Model #2004\n");
        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();
    }
}

可以用下面的代码进行测试:

/**
 * @author Tianlong Zhang
 * @time 2018/9/3 10:39
 * @project DesignPattern
 * @Version 1.0.0
 */
public class GumballMachineTestDrive {
    public static void main(String[] args){
        GumballMachine gumballMachine = new GumballMachine(5);

        System.out.println(gumballMachine);

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

        System.out.println(gumballMachine);

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

        System.out.println(gumballMachine);

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

        System.out.println(gumballMachine);

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

        System.out.println(gumballMachine);
    }
}

此时,运行结果为:

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 5 gumballs
Machine is waiting for quarter

You insert a quarter
You turned...
A gumball comes rolling out the slot

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 4 gumballs
Machine is waiting for quarter

You insert a quarter
Quarter returned
You turned but there's no quarter

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 4 gumballs
Machine is waiting for quarter

You insert a quarter
You turned...
A gumball comes rolling out the slot
You insert a quarter
You turned...
A gumball comes rolling out the slot
You haven't inserted a quarter

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 2 gumballs
Machine is waiting for quarter

You insert a quarter
You can't insert another quarter
You turned...
A gumball comes rolling out the slot
You insert a quarter
You turned...
A gumball comes rolling out the slot
Oops, out of gumballs
You can't insert a quarter, the machine is sold out
You turned , but there are no gumballs

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 0 gumballs
Machine is sold out

然而当我们还是面对着老话题,躲不开的话题,需求更改时,这样的设计方式就会显得捉襟见肘:此时的糖果公司由于竞争,需要给糖果机增加一个功能,每购买糖果10次,就可以获得一次竞赛的机会,出售糖果状态下会有10%的概率掉下两个糖果,所以对照我们上面的设计,是不是需要改动很多呢,不仅仅需要改动与竞赛相关的代码,原有的每个动作代码了里面的逻辑过程也都需要修改,因为此时有加入了一个新的状态,如果事件允许的话,可以自行尝试以下,这里就不再赘述了。

但是当我们换一种思考方式,类似于类的成员函数等与这个类的关系,本质上,类是定义了一个物体,而成员函数则大部分情况下为定义的这个物体的相关的动作。我们可以类比推理过来,可以将每个状态单独封装称一个类,用这个类来管理,这样就可以方便的管理状态,同时i也放也方便这个代码的维护和扩充,让我们不再去修改每部分代码的逻辑,住去修改相关,添加或删除相关的逻辑代码就可以了。所以我们放弃维护之前写过的代码,重新重构一个按照上述过程的代码:

首先定义一个接口,它规范了糖果机的行为,定义如下:

/**
 * @author Tianlong Zhang
 * @time 2018/9/3 10:43
 * @project DesignPattern
 * @Version 1.0.0
 */
public interface State {
    public void insertQuarter();
    public void ejectQuarter();
    public void turnCrank();
    public void dispense();

    public void refill();
}

因为每个动作都会使得状态发生改变,所以我们需要对每个状态分开来管理,使得尽量一个类管理一个状态,这样我们分别构建类4个类:

以出售状态为例,继承定义的接口,实现行为,完成相关的逻辑:

**
 * @author Tianlong Zhang
 * @time 2018/9/3 10:45
 * @project DesignPattern
 * @Version 1.0.0
 */
public class SoldState implements State {
    GumballMachine gumballMachine;

    public SoldState(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("Sorry, you already turned the crank");
    }

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

    public void dispense(){
        gumballMachine.releaseBall();
        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 "dispensing a gumball";
    }
}

类似的,根据业务的逻辑进行编写,分别完成4个状态:分别为:售罄(SoldOutState),没有投入25美分(NoQuarterState),投入了25美分(HasQuarterState),竞猜成功状态(WinnerState)。这里就不一样赘述,详细可以参考完整代码 GOGOGO

编写一个测试类进行测试:

/**
 * @author Tianlong Zhang
 * @time 2018/9/3 14:53
 * @project DesignPattern
 * @Version 1.0.0
 */
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();

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

        System.out.println(gumballMachine);
    }
}

运行结果和上面的结果相同,但是不同的是,我们通过一个类来管理一个状态的设计方式更好的进行了松耦合,能够最大程度的保证了代码的弹性。但是增加了游戏的运行结果如下所示:

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 10 gumballs
Machine is waiting for quarter

You inserted a quarter
You turned...
A gumball comes rolling out the slot...
You inserted a quarter
You turned...
A gumball comes rolling out the slot...

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 8 gumballs
Machine is waiting for quarter

You inserted a quarter
You turned...
A gumball comes rolling out the slot...
You inserted a quarter
You turned...
A gumball comes rolling out the slot...

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 6 gumballs
Machine is waiting for quarter

You inserted a quarter
You turned...
A gumball comes rolling out the slot...
You inserted a quarter
You turned...
A gumball comes rolling out the slot...

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 4 gumballs
Machine is waiting for quarter

You inserted a quarter
You turned...
A gumball comes rolling out the slot...
You inserted a quarter
You turned...
A gumball comes rolling out the slot...

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 2 gumballs
Machine is waiting for quarter

You inserted a quarter
You turned...
A gumball comes rolling out the slot...
You inserted a quarter
You turned...
A gumball comes rolling out the slot...
Oops, out of gumballs!

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 0 gumballs
Machine is sold out

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值