定义
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
(这个模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象,看起来好像修改了它的类。从客户角度来说,如果说你使用的对象能够完全改变它的行为,那么你会觉得,这个对象实际上是从别的类实例化而来的。然而实际上,你知道我们是在使用组合通过简单引用不同的状态对象来造成类改变的假象)
类图
一个小例子(来自四巨头的设计模式)
设计一个自动售货糖果机,和我们常见的自动售货机类似,它有几种状态,比如,等待投币的状态,出货的状态,商品售罄的状态等待,每种状态有不同的实现,状态之间可以互相替换,以转换成下一个合适的状态。现在我们结合状态设计模式来实现这个自动售货糖果机吧。
(1)创建状态接口。
/**
* 状态接口,所有的具体状态类都要实现这个接口
*/
public interface State {
/**
* 投币
*/
public void insertQuarter();
/**
* 退币
*/
public void ejectQuarter();
/**
* 转动曲柄
*/
public void turnCrank();
/**
* 发放糖果
*/
public void dispense();
/**
* 重新填充糖果
*/
public void refill();
}
(2)创建四种具体状态(分别为:等待投币状态,有币状态,售货状态,售罄状态)
/**
* 1,等待投币状态,实现了State接口,对里面的方法做相应的实现,以满足不同状态的变化要求。
* 其他状态类似。
*/
public class NoQuarterState implements State {
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("你投币了");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
public void ejectQuarter() {
System.out.println("你不能再投币");
}
public void turnCrank() {
System.out.println("转动曲柄,但是还没投币");
}
public void dispense() {
System.out.println("你需要首先投币");
}
public void refill() { }
public String toString() {
return "等待投币";
}
}
/**
* 2,有币的状态
*/
public class HasQuarterState implements State {
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("你不能再次投币");
}
public void ejectQuarter() {
System.out.println("货币返回给你");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
public void turnCrank() {
System.out.println("你转动曲柄。。。");
gumballMachine.setState(gumballMachine.getSoldState());
}
public void dispense() {
System.out.println("没有糖果分发");
}
public void refill() { }
public String toString() {
return "等待转动曲柄";
}
}
/**
* 3,售货状态
*/
public class SoldState implements State {
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("请等待,我们正在给你一颗糖果。");
}
public void ejectQuarter() {
System.out.println("抱歉,你已经转动过曲柄");
}
public void turnCrank() {
System.out.println("再次转动曲柄并不能得到另一颗糖果。");
}
public void dispense() {
gumballMachine.releaseBall();
if (gumballMachine.getCount() > 0) {
gumballMachine.setState(gumballMachine.getNoQuarterState());
} else {
System.out.println("糖果已经没了");
gumballMachine.setState(gumballMachine.getSoldOutState());
}
}
public void refill() { }
public String toString() {
return "分发一个糖果";
}
}
/**
* 4,糖果售罄状态
*/
public class SoldOutState implements State {
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
public void insertQuarter() {
System.out.println("你不能投币了,糖果机没有糖果了");
}
public void ejectQuarter() {
System.out.println("你不能得到糖果,你还没投币。");
}
public void turnCrank() {
System.out.println("转动曲柄,但是没有糖果。");
}
public void dispense() {
System.out.println("没有糖果分发");
}
public void refill() {
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
public String toString() {
return "售罄";
}
}
(3)创建GumballMachine糖果机(也就是Context)
/**
* 糖果机(也就是Context)
*/
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;
}
}
// 这些都是request()方法,当被调用的时候,内部实际上是调用的状态对象的方法。
public void insertQuarter() {
state.insertQuarter();
}
public void ejectQuarter() {
state.ejectQuarter();
}
public void turnCrank() {
state.turnCrank();
state.dispense();
}
void releaseBall() {
System.out.println("一个糖果正在出货。。。");
if (count != 0) {
count = count - 1;
}
}
int getCount() {
return count;
}
void refill(int count) {
this.count += count;
System.out.println("糖果机被重新填充,这是新的计数: " + 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("\n棒棒的糖果机");
result.append("\n库存: " + count + " 颗糖果");
result.append("\n");
result.append("机器是 " + state + "\n");
return result.toString();
}
}
(4)创建测试类
public class GumballMachineTestDrive {
public static void main(String[] args) {
GumballMachine gumballMachine = new GumballMachine(2);
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
gumballMachine.refill(5);
gumballMachine.insertQuarter();
gumballMachine.turnCrank();
System.out.println(gumballMachine);
}
}
(5)输出
棒棒的糖果机
库存: 2 颗糖果
机器是 等待投币
你投币了
你转动曲柄。。。
一个糖果正在出货。。。
棒棒的糖果机
库存: 1 颗糖果
机器是 等待投币
你投币了
你转动曲柄。。。
一个糖果正在出货。。。
糖果已经没了
你不能投币了,糖果机没有糖果了
转动曲柄,但是没有糖果。
没有糖果分发
糖果机被重新填充,这是新的计数: 5
你投币了
你转动曲柄。。。
一个糖果正在出货。。。
棒棒的糖果机
库存: 4 颗糖果
机器是 等待投币
小结
在状态模式中,定义了一组状态类,这些状态类都提供了它自己对于请求的具体实现。定义了一个Context类,客户之和这个Context类打交道。在Context类中,定义了一些内部状态对象,并实例化这些状态对象,也定义了一些request()方法,request()方法内部则调用的是状态类的方法(即request方法会把请求委托给状态对象来处理)。
(每一个状态是一个类,而每个类的功能不一样,当改变对状态对象的引用,Context的行为也就改变了)
(我们将一群行为封装在状态对象中,context的行为随时可以委托到那些状态对象中的一个,随着时间的流逝,当前状态在状态对象集合中游走,以反映出context内部的状态,因此,context的行为也会跟着改变。)