状态模式,允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。(通过这个模式将状态封装成独立的类,并将动作委托到代表当前状态的对象)
举个栗子:
有个糖果机控制器,有四种状态:东西售完(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,全程只有一种状态,在不断的切换)