一个糖果机,有四个状态,状态图如下:
最基本的实现思路如下:用四个整型数表示四种状态,用四个函数表达四个操作,每次进行操作的时候都要判断一下是否处于可进行这步操作的状态,操作完成后,需要更新状态到下一步。
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中,新增一个状态,只需要新增一个(表达这个状态的)类,并在该状态的“上游状态”做少许改动即可。