一個(gè)糖果機(jī),有四個(gè)狀態(tài),狀態(tài)圖如下:

最基本的實(shí)現(xiàn)思路如下:用四個(gè)整型數(shù)表示四種狀態(tài),用四個(gè)函數(shù)表達(dá)四個(gè)操作,每次進(jìn)行操作的時(shí)候都要判斷一下是否處于可進(jìn)行這步操作的狀態(tài),操作完成后,需要更新狀態(tài)到下一步。
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;
}
}
}
現(xiàn)在添加一個(gè)新的流程:當(dāng)turns crank的時(shí)候,判斷是否為1/10概率產(chǎn)生的幸運(yùn)兒,如果是,則彈出兩個(gè)糖果,如果不是,則仍彈出一個(gè)糖果。

實(shí)現(xiàn)如下:
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;
}
}
}
可見,為了添加一個(gè)流程,我們首先需要添加一個(gè)狀態(tài)SOLD_TO_WINNER = 4,然后在每個(gè)流程里面都要判斷一下是否處于這個(gè)狀態(tài),故而每個(gè)流程都添加了一個(gè)else if….
這樣的代碼,維護(hù)起來是可怕的,也就是說,這樣的設(shè)計(jì)思路是不易于擴(kuò)展的。
看到上面的程序,讓我想到了以前寫的“郵件收發(fā)程序”,繁復(fù)的else if…判斷語句讓我修改到最后,實(shí)在連看都不想看!不過好了,現(xiàn)在有了state pattern,專門處理怎樣寫業(yè)務(wù)流程。
State Pattern 的前提條件是:經(jīng)常發(fā)生改變的是狀態(tài)(也就是業(yè)務(wù)流程),而不是“操作”。在上面的例子中,我們把四個(gè)“操作”寫成了類,但發(fā)生變化的不是操作,而是if…else中的狀態(tài)。所以反其道而行之,我們把各個(gè)狀態(tài)寫成類(把易變化的隔離的單獨(dú)的類里面去)。如下:(未增加新狀態(tài)前)
package javaapplication42;
public class Main {
public static void main(String[] args) {
GumballMachine gm = new GumballMachine();
gm.addGumballs(2);
gm.state.insertQuarter();//并沒有指明是哪種狀態(tài),全部都用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 { //四個(gè)狀態(tài)都繼承它,這樣我們可以“代理”,每個(gè)狀態(tài)都有如下四個(gè)操作。
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;//機(jī)器內(nèi)糖果的數(shù)量
public GumballMachine() {
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
soldOutState = new SoldOutState(this);
this.state = soldOutState;//initialize "state",這個(gè)state將貫穿整個(gè)執(zhí)行過程
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);//執(zhí)行完后,改變狀態(tài)
}
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");
}
}

現(xiàn)在,我們新增SoldToWinnerState流程(1/10的概率獲得兩個(gè)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;//機(jī)器內(nèi)糖果的數(shù)量
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",這個(gè)state將貫穿整個(gè)執(zhí)行過程
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; //具體細(xì)節(jié),比如機(jī)器里只有一個(gè)糖果,暫不考慮,不是重點(diǎn)
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) { //產(chǎn)生0-9之間的隨機(jī)數(shù)
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中,新增一個(gè)狀態(tài),只需要新增一個(gè)(表達(dá)這個(gè)狀態(tài)的)類,并在該狀態(tài)的“上游狀態(tài)”做少許改動(dòng)即可。