设计模式之State Pattern 状态模式。

来源:互联网 发布:android 直播sdk 知乎 编辑:程序博客网 时间:2024/05/17 01:10
   一个糖果机,有四个状态,状态图如下:

最基本的实现思路如下:用四个整型数表示四种状态,用四个函数表达四个操作,每次进行操作的时候都要判断一下是否处于可进行这步操作的状态,操作完成后,需要更新状态到下一步。

package javaapplication41;

public class Main {

    public static void main(String[] args) {

        GumballMachine gm = new GumballMachine();

        gm.addGumballsToMachine(2);

        gm.insertQuarter();

        gm.turnsCrank();

        system.out.println("------------------");

        gm.insertQuarter();

        gm.insertQuarter();

        gm.turnsCrank();

        gm.ejectQuarter();

        gm.turnsCrank();

    }

}

class GumballMachine {

    final int SOLD_OUT = 0;

    final int NO_QUARTER = 1;

    final int HAS_QUARTER = 2;

    final int SOLD = 3;

    int state;

    int gumballs = 0;

    public GumballMachine() {

        state = SOLD_OUT;

    }

    public void insertQuarter() {

        if (state == NO_QUARTER) {

            system.out.println("inserting the quarter");

            state = HAS_QUARTER;

        }

        else if (state == HAS_QUARTER) {

            system.out.println("already has the quarter");

        }

        else if (state == SOLD) {

            system.out.println("already has the quarter and have turn the crank");

        }

        else if (state == SOLD_OUT) {

            system.out.println("there is no gumballs in the machine");

        }

    }

    public void ejectQuarter() {

        if (state == HAS_QUARTER) {

            system.out.println("ejecting the quarter....");

            state = NO_QUARTER;

        }

        else if (state == NO_QUARTER) {

            system.out.println("you haven't insert the quarter");

        }

        else if (state == SOLD) {

            system.out.println("already has the quarter and have turn the crank");

        }

        else if (state == SOLD_OUT) {

            system.out.println("there is no gumballs in the machine");

        }

    }

    public void turnsCrank() {

        if (state == HAS_QUARTER) {

            system.out.println("turning on the crank");

            state = SOLD;

            dispense();           

        }

        else if (state == NO_QUARTER) {

            system.out.println("you haven't insert the quarter");

        }

        else if (state == SOLD) {

            system.out.println("already has the quarter and have turn the crank");

        }

        else if (state == SOLD_OUT) {

            system.out.println("there is no gumballs in the machine");

        }

    }

    private void dispense() {

        if (state == SOLD) {

            if (gumballs == 0) {

                system.out.println("out of gumballs");

                state = SOLD_OUT;

            }

            else {

                system.out.println("dispensing the gumball");

                gumballs--;

                system.out.println("there are " + gumballs + " gumballs in the machine.");

                state = NO_QUARTER;

            }

        }

        else if (state == NO_QUARTER) {

            system.out.println("you haven't insert the quarter");

        }

        else if (state == HAS_QUARTER) {

            system.out.println("you haven't turn the crank");

        }

        else if (state == SOLD_OUT) {

            System.out.println("there is no gumballs in the machine");

        }

    }

    public void addGumballsToMachine(int num) {

        gumballs += num;

        system.out.println("adding " + num + " gumballs to the machine.");

        system.out.println("there are " + gumballs + " gumballs in the machine.");

        system.out.println("--------------------");

        if (gumballs > 0) {

            state = NO_QUARTER;

        }

    }

}

现在添加一个新的流程:当turns crank的时候,判断是否为1/10概率产生的幸运儿,如果是,则弹出两个糖果,如果不是,则仍弹出一个糖果。


实现如下:

package javaapplication41;

public class Main {

    public static void main(String[] args) {

        GumballMachine gm = new GumballMachine();

        gm.addGumballsToMachine(2);

        gm.insertQuarter();

        gm.turnsCrank();

        system.out.println("------------------");

        gm.insertQuarter();

        gm.insertQuarter();

        gm.turnsCrank();

        gm.ejectQuarter();

        gm.turnsCrank();

    }

}

class GumballMachine {

    final int SOLD_OUT = 0;

    final int NO_QUARTER = 1;

    final int HAS_QUARTER = 2;

    final int SOLD = 3;

    final int SOLD_TO_WINNER = 4;

    int state;

    int gumballs = 0;

    public GumballMachine() {

        state = SOLD_OUT;

    }

    public void insertQuarter() {

        if (state == NO_QUARTER) {

            system.out.println("inserting the quarter");

            state = HAS_QUARTER;

        }

        else if (state == HAS_QUARTER) {

            system.out.println("already has the quarter");

        }

        else if (state == SOLD) {

            system.out.println("already has the quarter and have turn the crank");

        }

        else if (state == SOLD_OUT) {

            system.out.println("there is no gumballs in the machine");

        }

        else if (state == SOLD_TO_WINNER) {

            system.out.println("already has the quarter and have turn the crank");

        }

    }

    public void ejectQuarter() {

        if (state == HAS_QUARTER) {

            system.out.println("ejecting the quarter....");

            state = NO_QUARTER;

        }

        else if (state == NO_QUARTER) {

            system.out.println("you haven't insert the quarter");

        }

        else if (state == SOLD) {

            system.out.println("already has the quarter and have turn the crank");

        }

        else if (state == SOLD_OUT) {

            system.out.println("there is no gumballs in the machine");

        }

        else if (state == SOLD_TO_WINNER) {

            system.out.println("already has the quarter and have turn the crank");

        }

    }

    public void turnsCrank() {

        if (state == HAS_QUARTER) {

            system.out.println("turning on the crank");

            state = SOLD;

            dispense();

        }

        else if (state == NO_QUARTER) {

            system.out.println("you haven't insert the quarter");

        }

        else if (state == SOLD) {

            system.out.println("already has the quarter and have turn the crank");

        }

        else if (state == SOLD_OUT) {

            system.out.println("there is no gumballs in the machine");

        }

        else if (state == SOLD_TO_WINNER) {

            system.out.println("already has the quarter and have turn the crank");

        }

    }

    private void dispense() {

        if (state == SOLD) {

            if (gumballs == 0) {

                system.out.println("out of gumballs");

                state = SOLD_OUT;

            }

            else {

                system.out.println("dispensing the gumball");

                gumballs--;

                system.out.println("there are " + gumballs + " gumballs in the machine.");

                state = NO_QUARTER;

            }

        }

        else if (state == SOLD_TO_WINNER) {

            if (gumballs == 0) {

                system.out.println("out of gumballs");

                state = SOLD_OUT;

            }

            else if (gumballs == 1) {

                system.out.println("dispensing the gumball");

                gumballs--;

                system.out.println("there are " + gumballs + " gumballs in the machine.");

                state = SOLD_OUT;

            }

            else if (gumballs > 1) {

                system.out.println("dispensing the 2 gumballs");

                gumballs -= 2;

                system.out.println("there are " + gumballs + " gumballs in the machine.");

                state = NO_QUARTER;

            }

        }

        else if (state == NO_QUARTER) {

            system.out.println("you haven't insert the quarter");

        }

        else if (state == HAS_QUARTER) {

            system.out.println("you haven't turn the crank");

        }

        else if (state == SOLD_OUT) {

            system.out.println("there is no gumballs in the machine");

        }

    }

    public void addGumballsToMachine(int num) {

        gumballs += num;

        system.out.println("adding " + num + " gumballs to the machine.");

        system.out.println("there are " + gumballs + " gumballs in the machine.");

        system.out.println("--------------------");

        if (gumballs > 0) {

            state = NO_QUARTER;

        }

    }

}

可见,为了添加一个流程,我们首先需要添加一个状态SOLD_TO_WINNER = 4,然后在每个流程里面都要判断一下是否处于这个状态,故而每个流程都添加了一个else if….

这样的代码,维护起来是可怕的,也就是说,这样的设计思路是不易于扩展的。

看到上面的程序,让我想到了以前写的“邮件收发程序”,繁复的else if…判断语句让我修改到最后,实在连看都不想看!不过好了,现在有了state pattern,专门处理怎样写业务流程。

State Pattern 的前提条件是:经常发生改变的是状态(也就是业务流程),而不是“操作”。在上面的例子中,我们把四个“操作”写成了类,但发生变化的不是操作,而是if…else中的状态。所以反其道而行之,我们把各个状态写成类(把易变化的隔离的单独的类里面去)。如下:(未增加新状态前)

package javaapplication42;

public class Main {

    public static void main(String[] args) {

        GumballMachine gm = new GumballMachine();

        gm.addGumballs(2);

        gm.state.insertQuarter();//并没有指明是哪种状态,全部都用state,这就是代理

        gm.state.turnCrank();

        gm.state.insertQuarter();

        gm.state.ejectQuarter();

        gm.state.turnCrank();

        gm.state.insertQuarter();

        gm.state.insertQuarter();

        gm.state.turnCrank();

        gm.state.insertQuarter();

        gm.state.turnCrank();

        gm.addGumballs(1);

        gm.state.insertQuarter();

        gm.state.turnCrank();

    }

}

interface State { //四个状态都继承它,这样我们可以“代理”,每个状态都有如下四个操作。

    public void insertQuarter();

    public void ejectQuarter();

    public void turnCrank();

    public void dispense();

}

class GumballMachine {

    State state;

    State noQuarterState;

    State hasQuarterState;

    State soldState;

    State soldOutState;

    int gumballNum;//机器内糖果的数量

    public GumballMachine() {

        noQuarterState = new NoQuarterState(this);

        hasQuarterState = new HasQuarterState(this);

        soldState = new SoldState(this);

        soldOutState = new SoldOutState(this);

        this.state = soldOutState;//initialize "state",这个state将贯穿整个执行过程

        gumballNum = 0;

    }

    public void setState(State state) {

        this.state = state;

    }

    public void play() {

        state.insertQuarter();

        state.turnCrank();

        state.dispense();

    }

    public void addGumballs(int num) {

        gumballNum += num;

        if (gumballNum > 0) {

            this.state = noQuarterState;

        }

        system.out.println("the machine has "+gumballNum+" gumball(s)");

    }

}

class NoQuarterState implements State {

    GumballMachine gm;

    public NoQuarterState(GumballMachine gm) {

        this.gm = gm;

    }

    public void insertQuarter() {

       System.out.println("insert a quarter...");

        gm.setState(gm.hasQuarterState);//执行完后,改变状态

    }

    public void ejectQuarter() {

        system.out.println("you can't eject quarter, for you haven't insert yet 1");

    }

    public void turnCrank() {

        system.out.println("you can't turn crank, for you haven't insert yet 2");

    }

    public void dispense() {

        system.out.println("you can't dispense, for you haven't insert yet 3");

    }

}

class HasQuarterState implements State {

    GumballMachine gm;

    public HasQuarterState(GumballMachine gm) {

        this.gm = gm;

    }

    public void insertQuarter() {

        system.out.println("you can't insert quarter, for you have insert one already");

    }

    public void ejectQuarter() {

        system.out.println("eject quarter...");

        gm.setState(gm.noQuarterState);

    }

    public void turnCrank() {

        system.out.println("turning the crank...");

        gm.setState(gm.soldState);

        gm.state.dispense();

    }

    public void dispense() {

        system.out.println("when you turn the crank, the machine will dispense the gumball");

    }

}

class SoldState implements State {

    GumballMachine gm;

    public SoldState(GumballMachine gm) {

        this.gm = gm;

    }

    public void insertQuarter() {

        throw new UnsupportedOperationException("Not supported yet.");

    }

    public void ejectQuarter() {

        throw new UnsupportedOperationException("Not supported yet.");

    }

    public void turnCrank() {

        throw new UnsupportedOperationException("Not supported yet.");

    }

    public void dispense() {

        gm.gumballNum--;

        system.out.println("dispense one gumball...");

        if (gm.gumballNum > 0) {

            gm.setState(gm.noQuarterState);

        }

        else {

            system.out.println("Machine has no gumball");

            gm.setState(gm.soldOutState);

        }

    }

}

class SoldOutState implements State {

    GumballMachine gm;

    public SoldOutState(GumballMachine gm) {

        this.gm = gm;

    }

    public void insertQuarter() {

        system.out.println("you can't insert quarter, for the machine has no gumball");

    }

    public void ejectQuarter() {

        system.out.println("you can't eject quarter, for the machine has no gumball");

    }

    public void turnCrank() {

        system.out.println("you can't turn crank, for the machine has no gumball");

    }

    public void dispense() {

        system.out.println("you can't dispense, for the machine has no gumball");

    }

}


现在,我们新增
SoldToWinnerState流程(1/10的概率获得两个gumball):

package javaapplication42;

import java.util.Random;

public class Main {

    public static void main(String[] args) {

        GumballMachine gm = new GumballMachine();

        gm.addGumballs(2);

        gm.state.insertQuarter();

        gm.state.turnCrank();

        gm.state.insertQuarter();

        gm.state.ejectQuarter();

        gm.state.turnCrank();

        gm.state.insertQuarter();

        gm.state.insertQuarter();

        gm.state.turnCrank();

        gm.state.insertQuarter();

        gm.state.turnCrank();

        gm.addGumballs(1);

        gm.state.insertQuarter();

        gm.state.turnCrank();

    }

}

interface State {

    public void insertQuarter();

    public void ejectQuarter();

    public void turnCrank();

    public void dispense();

}

class GumballMachine {

    State state;

    State noQuarterState;

    State hasQuarterState;

    State soldState;

    State soldOutState;

    State soldToWinnerState;

    int gumballNum;//机器内糖果的数量

    public GumballMachine() {

        noQuarterState = new NoQuarterState(this);

        hasQuarterState = new HasQuarterState(this);

        soldState = new SoldState(this);

        soldOutState = new SoldOutState(this);

        soldToWinnerState = new SoldToWinnerState(this);

        this.state = soldOutState;//initialize "state",这个state将贯穿整个执行过程

        gumballNum = 0;

    }

    public void setState(State state) {

        this.state = state;

    }

    public void play() {

        state.insertQuarter();

        state.turnCrank();

        state.dispense();

    }

    public void addGumballs(int num) {

        gumballNum += num;

        if (gumballNum > 0) {

            this.state = noQuarterState;

        }

        system.out.println("the machine has " + gumballNum + " gumball(s)");

    }

}

class SoldToWinnerState implements State {

    GumballMachine gm;

    public SoldToWinnerState(GumballMachine gm) {

        this.gm = gm;

    }

    public void insertQuarter() {

        system.out.println("you have insert one quarter already");

    }

    public void ejectQuarter() {

        system.out.println("you have turn crank already");

    }

    public void turnCrank() {

        system.out.println("you have turn crank already");

    }

    public void dispense() {

        gm.gumballNum -= 2; //具体细节,比如机器里只有一个糖果,暂不考虑,不是重点

        system.out.println("*************you are winner!************");

        System.out.println("dispense two gumball...");

        if (gm.gumballNum > 0) {

            gm.setState(gm.noQuarterState);

        }

        else {

            system.out.println("Machine has no gumball");

            gm.setState(gm.soldOutState);

       }

    }

}

class NoQuarterState implements State {

    GumballMachine gm;

    public NoQuarterState(GumballMachine gm) {

        this.gm = gm;

    }

    public void insertQuarter() {

        system.out.println("insert a quarter...");

        gm.setState(gm.hasQuarterState);

    }

    public void ejectQuarter() {

        system.out.println("you can't eject quarter, for you haven't insert yet 1");

    }

    public void turnCrank() {

        system.out.println("you can't turn crank, for you haven't insert yet 2");

    }

    public void dispense() {

        system.out.println("you can't dispense, for you haven't insert yet 3");

    }

}

class HasQuarterState implements State {

    Random rm;

    GumballMachine gm;

    public HasQuarterState(GumballMachine gm) {

        this.gm = gm;

        rm = new Random();

    }

    public void insertQuarter() {

        system.out.println("you can't insert quarter, for you have insert one already");

    }

    public void ejectQuarter() {

        system.out.println("eject quarter...");

        gm.setState(gm.noQuarterState);

    }

    public void turnCrank() {

        system.out.println("turning the crank...");

        if (rm.nextFloat() * 10 == 9) { //产生0-9之间的随机数

            gm.setState(gm.soldToWinnerState);

        }

        else {

            gm.setState(gm.soldState);

        }

        gm.state.dispense();

    }

    public void dispense() {

        system.out.println("when you turn the crank, the machine will dispense the gumball");

    }

}

class SoldState implements State {

    GumballMachine gm;

    public SoldState(GumballMachine gm) {

        this.gm = gm;

    }

    public void insertQuarter() {

        throw new UnsupportedOperationException("Not supported yet.");

    }

    public void ejectQuarter() {

        throw new UnsupportedOperationException("Not supported yet.");

    }

    public void turnCrank() {

        throw new UnsupportedOperationException("Not supported yet.");

    }

    public void dispense() {

        gm.gumballNum--;

        system.out.println("dispense one gumball...");

        if (gm.gumballNum > 0) {

            gm.setState(gm.noQuarterState);

        }

        else {

            system.out.println("Machine has no gumball");

            gm.setState(gm.soldOutState);

        }

    }

}

class SoldOutState implements State {

    GumballMachine gm;

    public SoldOutState(GumballMachine gm) {

        this.gm = gm;

    }

    public void insertQuarter() {

        system.out.println("you can't insert quarter, for the machine has no gumball");

    }

    public void ejectQuarter() {

        system.out.println("you can't eject quarter, for the machine has no gumball");

    }

    public void turnCrank() {

        system.out.println("you can't turn crank, for the machine has no gumball");

    }

    public void dispense() {

        system.out.println("you can't dispense, for the machine has no gumball");

    }

}


可见,在
state pattern中,新增一个状态,只需要新增一个(表达这个状态的)类,并在该状态的“上游状态”做少许改动即可。