设计模式中的设计思想、图片和部分代码参考自《Head First设计模式》,作者Eric Freeman & Elisabeth Freeman & Kathy Siezza & Bert Bates。
在这里我只是对这本书进行学习阅读,并向大家分享一些心得体会。
有一家糖果机公司想让我们帮他们做一套软件系统供糖果机使用,并且他们想要达成如下效果。
这是一张状态图,每个圆圈表示糖果机的一种状态,每个箭头表示状态的转换过程。例如对于糖果机来说,我们投入25分钱,那么糖果机就由"没有25分钱" ——> "有25分钱"这个状态。
第一步:
列出糖果机所有可能的状态。经过分析,可以得到如下四种状态:
没有25分钱
有25分钱
糖果售空
售出糖果
第二步:
为每个状态都赋予一个常量值,并且再定义一个变量,表示当前所处的状态:
//售空
final static int SOLD_OUT = 0;
//无硬币
final static int NO_QUARTER = 1;
//有硬币
final static int HAS_QUARTER = 2;
//售出
final static int SOLD = 3;
//糖果机目前所处的状态,初始时默认为无糖果的状态
int state = SOLD_OUT;
第三步:
列出糖果所可能接收到一切动作(指令):
投入25分钱
退回25分钱
转动曲柄
发放糖果
第四步:
创建一个类,这个类的作用就像是一个状态机。对每一个动作( 投入25分钱 ),都需要创建一个对应的方法( insertQuarter() )。
但是糖果机的状态不同,即使接收到相同的动作,那么结果也是不同的。所以在每个方法中,都需要用if分支做判断,根据当前所处的状态,来判断接收到此指令后下个状态是什么。
//投入硬币
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 inserted 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 class GumballMachine {
//售空
final static int SOLD_OUT = 0;
//无硬币
final static int NO_QUARTER = 1;
//有硬币
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;
}
}
//----------------------四种动作----------------------
//投入硬币
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 inserted 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 ejectQuarter() {
if (state == HAS_QUARTER) {
System.out.println("Quarter returned");
state = NO_QUARTER;
} else if (state == NO_QUARTER) {
System.out.println("You haven't inserted a quarter");
} else if (state == SOLD) {
System.out.println("Sorry, you already turned the crank");
} else if (state == SOLD_OUT) {
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
}
//转动曲柄
public void turnCrank() {
if (state == SOLD) {
System.out.println("Turning twice doesn't get you another gumball!");
} else if (state == NO_QUARTER) {
System.out.println("You turned but there's no quarter");
} else if (state == SOLD_OUT) {
System.out.println("You turned, but there are no gumballs");
} else if (state == HAS_QUARTER) {
System.out.println("You turned...");
state = SOLD;
dispense();
}
}
//发放糖果
private 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_QUARTER;
}
} else if (state == NO_QUARTER) {
System.out.println("You need to pay first");
} else if (state == SOLD_OUT) {
System.out.println("No gumball dispensed");
} else if (state == HAS_QUARTER) {
System.out.println("No gumball dispensed");
}
}
//----------------------其他方法----------------------
//充值
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();
}
}
测试方法:
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 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
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 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
You haven't inserted a quarter
Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 2 gumballs
Machine is waiting for quarter
You inserted a quarter
You can't insert another 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!
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%的几率掉出两个糖果。
虽然我们设计的系统周详、稳定,但是这并不意味着我们的系统就容易扩展。
如果在现有系统上实现,那么就得变成这样:
变更分析:
很遗憾,我们必须得重构代码了,坚持用现有代码,会增加程序bug的几率。
我们应该使者局部化每个状态的行为,这样一来,如果我们针对某个状态做了改变,就不会影响其他代码。我们需要遵守"封装变化"的原则。如果我们将每个变化的行为都放在各自的类中,那么每个状态只要实现它自己的动作就可以了。
这样增加新状态时,虽然我们仍然需要修改代码,但是我们将修改局限在一个很小的范围内,加入一个新状态,我们只需加入一个类还有可能改变一些转换即可。
定义状态接口:
public interface State {
//投入硬币
public void insertQuarter();
//退出硬币
public void ejectQuarter();
//转动曲柄
public void turnCrank();
//发放糖果
public void dispense();
//充值
public void refill();
}
定义状态类-无硬币状态:
public class NoQuarterState implements State {
//糖果机
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("You inserted a quarter");
//在"没有硬币"的状态下,投入硬币,糖果机设置新状态为"有硬币"
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
public void ejectQuarter() {
System.out.println("You haven't inserted a quarter");
}
public void turnCrank() {
System.out.println("You turned, but there's no quarter");
}
public void dispense() {
System.out.println("You need to pay first");
}
public void refill() { }
public String toString() {
return "waiting for quarter";
}
}
定义状态类-有硬币状态:
public class HasQuarterState implements State {
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("You can't insert another quarter");
}
public void ejectQuarter() {
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
public void turnCrank() {
System.out.println("You turned...");
gumballMachine.setState(gumballMachine.getSoldState());
}
public void dispense() {
System.out.println("No gumball dispensed");
}
public void refill() { }
public String toString() {
return "waiting for turn of crank";
}
}
定义状态类-售空状态:
public class SoldOutState implements State {
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("You can't insert a quarter, the machine is sold out");
}
public void ejectQuarter() {
System.out.println("You can't eject, you haven't inserted a quarter yet");
}
public void turnCrank() {
System.out.println("You turned, but there are no gumballs");
}
public void dispense() {
System.out.println("No gumball dispensed");
}
public void refill() {
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
public String toString() {
return "sold out";
}
}
定义状态类-售出状态:
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";
}
}
定义糖果机:
糖果机的状态已不再由常量表示,而是由各种State对象表示,这些State对象也是糖果机动作的委托者。
代码:
public class GumballMachine {
//四种状态
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
//糖果机当前所处状态
State state;
int count = 0;
public GumballMachine(int numberGumballs) {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
this.count = numberGumballs;
if (numberGumballs > 0) {
state = noQuarterState;
} else {
state = soldOutState;
}
}
/*
* insertQuarter、
* ejectQuarter、
* turnCrank
* 都委托给当前对象state来处理
*/
public void insertQuarter() {
state.insertQuarter();
}
public void ejectQuarter() {
state.ejectQuarter();
}
public void turnCrank() {
state.turnCrank();
//方法糖果是糖果机的内部方法,不允许用户调用
state.dispense();
}
void releaseBall() {
System.out.println("A gumball comes rolling out the slot...");
if (count != 0) {
count = count - 1;
}
}
int getCount() {
return count;
}
void refill(int count) {
this.count += count;
System.out.println("The gumball machine was just refilled; it's new count is: " + this.count);
state.refill();
}
//设置机器当前的状态
void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public State getSoldOutState() {
return soldOutState;
}
public State getNoQuarterState() {
return noQuarterState;
}
public State getHasQuarterState() {
return hasQuarterState;
}
public State getSoldState() {
return soldState;
}
public String toString() {
StringBuffer result = new StringBuffer();
result.append("\nMighty Gumball, Inc.");
result.append("\nJava-enabled Standing Gumball Model #2004");
result.append("\nInventory: " + count + " gumball");
if (count != 1) {
result.append("s");
}
result.append("\n");
result.append("Machine is " + state + "\n");
return result.toString();
}
}
测试程序:
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 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
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 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
You haven't inserted a quarter
Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 2 gumballs
Machine is waiting for quarter
You inserted a quarter
You can't insert another 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!
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
在"有硬币"的状态下转动曲柄:
将由"有硬币"状态转换到"售出"状态:
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
这个描述的前半句:因为这个模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象,我们知道行为会随着内部状态而改变。糖果机提供了一个很好的例子:当糖果机实在"无硬币"或者"有硬币"的两种状态下,这时投入硬币,会得到不同的行为(机器接收硬币或机器拒绝硬币)。
这个描述的后半句:从客户的视角看:如果说你使用的对象能够完全改变它的行为,那么你会觉得,这个对象实际上是从别的类实例化而来的。然而,实际上,我们知道,我们是在使用组合通过简单引用不同的状态对象造成类改变的假象。
观察这个类图,我们发现和"策略模式"很是相像,但是差别在于它们的"意图"不同。
状态模式:
对于状态模式而言,我们将一群行为封装在状态对象在,context(糖果机)的行为随时可委托到那些状态对象中的额一个。随着时间的流逝,当前状态在状态对象集合中游走改变,以反映出context内部的状态,因此context的行为也会跟着改变,但是context的客户对于状态对象了解不多,甚至浑然不觉。
我们把状态模式想成是不用在context中放置许多条件判断的替代方案。通过将行为包装进状态对象中,我们可以通过在context内简单的改变状态对象来改变context的行为。
策略模式:
对于策略模式而言,客户通常主动指定Context所要组合的策略对象是哪一个。现在,固然策略模式让我们具有弹性,能够在运行时改变策略,但对于某个context对象来说,通常都只要一个最适当的策略对象。比方说,在第一章的策略模式中,有些鸭子(绿头鸭)被设置成利用典型的飞翔行为进行飞翔,而有些鸭子(例如橡皮鸭)使用的飞翔行为只能让他们贴地飞行。
一般来说我们把策略模式想成除了继承之外的一种弹性替代方案。如果我们使用继承定义了一个类的行为,我们将被这个行为困住,甚至要修改它都很难。有了策略模式,我们通过组合不同的对象来改变行为。
1)问:在GumballMachine中,状态决定了下一个状态应该是什么。ConcreteState总是决定接下来状态是什么吗?
答:并不总是如此,context也可以决定状态转换的流向。一般来说,当状态转换是固定的时候,就适合放在context中;然而当状态转换是更动态的时候,通常就会放在状态类在(这就是本文的实现方法,例如,在GumballMachine中,由运行时糖果的数目来决定状态要转换到"无硬币"还是"售空")。将状态转换放在状态类中的缺点是:状态类之间产生了依赖。在我们的GumballMachine实现中,我们试图通过使用Context上的getter方法将依赖见到最小,而不是显示的硬编码具体的状态类。状态转换放到哪一个类中,还会影响另一方面,就是我们究竟要对哪个类做"修改关闭",是Context还是状态类)。
2)问:客户会直接和状态交互吗?
答:不会。状态是用在Context中代表它的内部状态以及行为的,所以只有Context才会对状态提出请求。客户不会直接改变Context的状态。全盘了解状态时Context的工作。
3)问:如果在我们的程序中Context由许多实例,这些实例之间可以共享对象吗?
答:绝对可以,事实上这是很常见的做法。但唯一的前提是,我们的状态对象不能持有它们自己的内部状态;否则就不能共享。
想要共享状态,我们需要把每个状态都指定到静态的实例变量中。如果我们的状态需要利用Context中的方法或者实例变量,我们能还需再每个handler()方法内传入一个context的引用。
4)问:使用状态模式似乎总是增加我们设计中类的数目。GumballMachine中,新版比旧版多了很多类。
答:是的,在个别的状态类中封装状态行为,结果总是增加这个设计中类的数目。这就是为了要获取弹性而付出的代价。除非我们的代码时一次性的。可以用完就扔,但是这不可能,那么其实状态模式的设计是绝对值得的。其实真正重要的是我们暴露给客户的类的数目,而且我们有办法将这些额外的状态全部都隐藏起来。举例:如果我们有一个应用,它有很多状态,但是我们决定不将这些状态封装在不同的对象中,那么我们就必须设计巨大的、整块的条件语句。这会让我们的代码不易维护和理解。通过使用许多对象,我们可以让状态变得干净。
之前糖果公司要求有10%掉出两个糖果,现在来实现这个需求。
GumballMachine:
public class GumballMachine {
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
//新增人生赢家这个状态(其实就是掉出两个糖果)
State winnerState;
State state = soldOutState;
int count = 0;
public GumballMachine(int numberGumballs) {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
winnerState = new WinnerState(this);
this.count = numberGumballs;
if (numberGumballs > 0) {
state = noQuarterState;
}
}
public void insertQuarter() {
state.insertQuarter();
}
public void ejectQuarter() {
state.ejectQuarter();
}
public void turnCrank() {
state.turnCrank();
state.dispense();
}
void setState(State state) {
this.state = state;
}
void releaseBall() {
System.out.println("A gumball comes rolling out the slot...");
if (count != 0) {
count = count - 1;
}
}
int getCount() {
return count;
}
void refill(int count) {
this.count += count;
System.out.println("The gumball machine was just refilled; it's new count is: " + this.count);
state.refill();
}
public State getState() {
return state;
}
public State getSoldOutState() {
return soldOutState;
}
public State getNoQuarterState() {
return noQuarterState;
}
public State getHasQuarterState() {
return hasQuarterState;
}
public State getSoldState() {
return soldState;
}
public State getWinnerState() {
return winnerState;
}
public String toString() {
StringBuffer result = new StringBuffer();
result.append("\nMighty Gumball, Inc.");
result.append("\nJava-enabled Standing Gumball Model #2004");
result.append("\nInventory: " + count + " gumball");
if (count != 1) {
result.append("s");
}
result.append("\n");
result.append("Machine is " + state + "\n");
return result.toString();
}
}
定义状态类-人生赢家状态:
public class WinnerState implements State {
GumballMachine gumballMachine;
public WinnerState(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("Please wait, we're already giving you a Gumball");
}
public void turnCrank() {
System.out.println("Turning again doesn't get you another gumball!");
}
public void dispense() {
gumballMachine.releaseBall();
if (gumballMachine.getCount() == 0) {
gumballMachine.setState(gumballMachine.getSoldOutState());
} else {
gumballMachine.releaseBall();
System.out.println("YOU'RE A WINNER! You got two gumballs for your quarter");
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 "despensing two gumballs for your quarter, because YOU'RE A WINNER!";
}
}
修改状态类-有硬币状态:
public class HasQuarterState implements State {
//随机数产生器
Random randomWinner = new Random(System.currentTimeMillis());
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("You can't insert another quarter");
}
public void ejectQuarter() {
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
public void turnCrank() {
System.out.println("You turned...");
/*
* 顾客有10%几率获胜
* 获胜:进入认识赢家状态
* 未获胜:进入售出状态
*/
int winner = randomWinner.nextInt(10);
if ((winner == 0) && (gumballMachine.getCount() > 1)) {
gumballMachine.setState(gumballMachine.getWinnerState());
} else {
gumballMachine.setState(gumballMachine.getSoldState());
}
}
public void dispense() {
System.out.println("No gumball dispensed");
}
public void refill() { }
public String toString() {
return "waiting for turn of crank";
}
}
测试代码:
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();
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
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...
A gumball comes rolling out the slot...
YOU'RE A WINNER! You got two gumballs for your quarter
You inserted a quarter
You turned...
A gumball comes rolling out the slot...
Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 7 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...
A gumball comes rolling out the slot...
YOU'RE A WINNER! You got two gumballs for your quarter
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
You can't insert a quarter, the machine is sold out
You turned, but there are no gumballs
No gumball dispensed
You can't insert a quarter, the machine is sold out
You turned, but there are no gumballs
No gumball dispensed
Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 0 gumballs
Machine is sold out
在我们的SoldOutState和WinnerState这两个状态之间,有许多重复的代码,我们可以考虑把State接口设计为抽象类,然后将方法的默认行为放在其中。