状态模式:允许对象在内部状态改变时改变他的行为,对象看起来好像修改了它的类。
有一个糖果机,它的状态图是这样的:
我们来实现糖果机:
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;//如果库存不为零,机器进入 没有25分钱状态
}
public void insertQuarter(){
if(state==HAS_QUARTER)
System.out.println("You cannot 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 cannont 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 insert a quarter.");
}else if (state==SOLD) {
System.out.println("Sorry,you already turned the crank.");
}else if (state==SOLD_OUT) {
System.out.println("You cannot eject,you haven't insert 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 is no quarter.");
}else if (state==SOLD_OUT) {
System.out.println("You turned,but there is no gumball.");
}else if (state==HAS_QUARTER) {
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_QUARTER;
}
}else if (state==NO_QUARTER) {
System.out.println("You need to pay first");
}else if(state==SOLD_OUT){
System.out.println("No gumballs dispensed");
}else if (state==HAS_QUARTER) {
System.out.println("No gumball dispensed");
}
}
//其他方法
}
然后糖果公司有变更请求了:当曲柄被转动时,有10%几率掉下来两个糖果。
这样一来,以上的版本就不再适用了。
我们的计划是这样的:不再维护现有代码,重写他以便于将状态对象封装在各自的类,然后在动作发生时委托给当前状态。
首先,定义一个State接口,有4个方法对应4个动作
然后为每个状态实现状态类
public class NoQuarterState implements State{
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine){
this.gumballMachine=gumballMachine;
}
@Override
public void insertQuarter() {
// TODO Auto-generated method stub
System.out.println("You insert a quarter");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
@Override
public void ejectQuarter() {
// TODO Auto-generated method stub
System.out.println("You haven't insert a quarter");
}
@Override
public void turnCrank() {
// TODO Auto-generated method stub
System.out.println("You turned,but there's no quarter");
}
@Override
public void dispense() {
// TODO Auto-generated method stub
System.out.println("You need to pay first");
}
}
……
糖果机类:
public class GumballMachine {
//四个状态
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State state=soldOutState;//跟踪当前状态,初始为 售罄
int count=0;//追踪机器内的糖果数量
public GumballMachine(int count){
soldOutState=new SoldOutState(this);
noQuarterState=new NoQuarterState(this);
hasQuarterState=new HasQuarterState(this);
soldState=new SoldState(this);
this.count=count;
if(count>0)
state=noQuarterState;//如果库存不为零,机器进入 没有25分钱状态
}
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;
}
//其他方法,getter
}
解决十次抽中一次的游戏:
首先在GumballMachine类中加入winnerState的状态然后初始化
在实现WinnerState本身
public class WinnerState implements State{
//实例变量和构造器
//insertQuarter错误信息
//ejectQuarter错误信息
//turnCrank错误信息
@Override
public void dispense() {
// TODO Auto-generated method stub
System.out.println("You are a winner!");
gumballMachine=releaseBall();
if(gumballMachine.getCount()==0)
gumballMachine.setState(gumballMachine.getSoldOutState());
else {
gumballMachine.releaseBall();
if(gumballMachine.getCount()>0)
gumballMachine.setState(gumballMachine.getNoQuarterState());
else {
System.out.println("Oops,out of gumballs!");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
}
在HasQuarterState中加入这个变化
public class HasQuarterState implements State{
Random randomWinner=new Random(System.currentTimeMillis());
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine){
this.gumballMachine=gumballMachine;
}
@Override
public void insertQuarter() {
// TODO Auto-generated method stub
System.out.println("You can't insert another quarter");
}
@Override
public void ejectQuarter() {
// TODO Auto-generated method stub
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
@Override
public void turnCrank() {
// TODO Auto-generated method stub
System.out.println("YOu turned...");
int winner=randomWinner.nextInt(10);
if(winner==0 && gumballMachine.getCount()>1){
gumballMachine.setState(gumballMachine.getWinnerState());
}else {
gumballMachine.setState(gumballMachine.getSoldState());
}
}
@Override
public void dispense() {
// TODO Auto-generated method stub
System.out.println("No gumball dispensed");
}
}
OK 大功告成!
我发现状态模式和策略模式结构上很像,但是他们的意图不一样。策略是外部决定采取何种策略,状态是内部决定他们的状态变迁,外界只关心接口。