一.概念
状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式。
状态模式允许一个对象在其内部状态改变的时候改变其行为。这个对象看上去就像是改变了它的类一样。
状态模式的结构
用一句话来表述,状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式的示意性类图如下所示:
状态模式所涉及到的角色有:
● 环境(Context)角色,也成上下文:定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。
● 抽象状态(State)角色:定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。
● 具体状态(ConcreteState)角色:每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。
源代码
环境角色类
public class Context { //持有一个State类型的对象实例 private State state; public void setState(State state) { this.state = state; } /** * 用户感兴趣的接口方法 */ public void request(String sampleParameter) { //转调state来处理 state.handle(sampleParameter); } }
抽象状态类
public interface State { /** * 状态对应的处理 */ public void handle(String sampleParameter); }
具体状态类
public class ConcreteStateA implements State { @Override public void handle(String sampleParameter) { System.out.println("ConcreteStateA handle :" + sampleParameter); } }
public class ConcreteStateB implements State { @Override public void handle(String sampleParameter) { System.out.println("ConcreteStateB handle :" + sampleParameter); } }
客户端类
public class Client { public static void main(String[] args){ //创建状态 State state = new ConcreteStateB(); //创建环境 Context context = new Context(); //将状态设置到环境中 context.setState(state); //请求 context.request("test"); } }
从上面可以看出,环境类Context的行为request()是委派给某一个具体状态类的。通过使用多态性原则,可以动态改变环境类Context的属性State的内容,使其从指向一个具体状态类变换到指向另一个具体状态类,从而使环境类的行为request()由不同的具体状态类来执行。
二、head first中的例子
糖果机共有5个状态:没有25分钱、有25分钱、售出糖果、糖果售罄、赢家状态,这里从下面的UML状态图可以看到,Visio里使用椭圆形表示状态,还有引发糖果机状态发生转换的事件动作。
我使用Visio2003绘制的糖果机状态图,画得不太好,可能不是很标准,UML对于面向对象的软件设计很重要。
对应的Java代码如下:
- //State.java
- package State;
- /*
- * 状态接口State
- */
- public interface State {
- public void insertQuarter(); // 投入25分钱
- public void ejectQuarter(); // 拒绝25分钱
- public void turnCrank(); // 转动曲柄
- public void dispense(); // 发放糖果
- }
- //NoQuarterState.java
- package State;
- import State.GumballMachine;
- /*
- * 没有25分钱状态,实现了State接口
- */
- public class NoQuarterState implements State{
- GumballMachine gumballMachine;
- public NoQuarterState(GumballMachine gumballMachine){
- this.gumballMachine=gumballMachine;
- }
- // 投入25分钱
- public void insertQuarter() {
- System.out.println("You insert a quarter");
- gumballMachine.setState(gumballMachine.getHasQuarterState());
- }
- // 拒绝25分钱
- public void ejectQuarter() {
- System.out.println("You haven't insert a quarter");
- }
- // 转动曲柄
- public void turnCrank() {
- System.out.println("You turned crank,but you there's no quarter");
- }
- // 发放糖果
- public void dispense() {
- System.out.println("You need to pay first");
- }
- }
- //HasQuarterState.java
- package State;
- import java.util.Random;
- import State.GumballMachine;
- /*
- * 有25分钱状态,实现了State接口
- */
- public class HasQuarterState implements State {
- Random randomWinner = new Random(System.currentTimeMillis()); //首先我们增加一个随机数产生器,产生10%赢的机
- 会
- GumballMachine gumballMachine;
- public HasQuarterState(GumballMachine gumballMachine){
- this.gumballMachine = gumballMachine;
- }
- // 投入25分钱
- public void insertQuarter() {
- System.out.println("You can not insert anther quarter");
- }
- // 拒绝25分钱
- public void ejectQuarter() {
- System.out.println("Quarter returned");
- gumballMachine.setState(gumballMachine.getNoQuarterState());
- }
- // 转动曲柄
- public void turnCrank() {
- System.out.println("You turned...");
- int winner = randomWinner.nextInt(10);
- System.out.println("winner =" + winner);
- if((winner ==0) && (gumballMachine.getCount() > 1)) {
- gumballMachine.setState(gumballMachine.getWinnerState());
- } else {
- gumballMachine.setState(gumballMachine.getSoldState());
- }
- }
- // 发放糖果
- public void dispense() {
- System.out.println("No gumball dispensed");
- }
- }
- //SoldState.java
- package State;
- import State.GumballMachine;
- /*
- * 售出糖果状态,实现了State接口
- */
- public class SoldState implements State{
- GumballMachine gumballMachine;
- public SoldState(GumballMachine gumballMachine) {
- this.gumballMachine = gumballMachine;
- }
- // 投入25分钱
- public void insertQuarter() {
- System.out.println("Please wait, we're already giving you a gumball");
- }
- // 拒绝25分钱
- public void ejectQuarter() {
- System.out.println("Sorry,you have already turn crank");
- }
- // 转动曲柄
- public void turnCrank() {
- System.out.println("trun twice ,doesn't give you anthor gamball!");
- }
- // 发放糖果
- public void dispense() {
- gumballMachine.releaseBall();
- if(gumballMachine.getCount()>0){
- gumballMachine.setState(gumballMachine.getNoQuarterState());
- } else {
- System.out.println("Opps,out of gamball!");
- gumballMachine.setState(gumballMachine.getSoldOutState());
- }
- }
- }
- //SoldOutState.java
- package State;
- import State.GumballMachine;
- /*
- * 通过售罄状态,实现了State接口
- */
- public class SoldOutState implements State{
- GumballMachine gumballMachine;
- public SoldOutState(GumballMachine gumballMachine){
- this.gumballMachine=gumballMachine;
- }
- // 投入25分钱
- public void insertQuarter() {
- System.out.println("You can't insert a quarter, the machine is sold out");
- }
- // 拒绝25分钱
- public void ejectQuarter() {
- // TODO Auto-generated method stub
- System.out.println("You can't eject, you haven't inserted a quarter yet");
- }
- // 转动曲柄
- public void turnCrank() {
- // TODO Auto-generated method stub
- System.out.println("You turned, but there are no gumballs");
- }
- // 发放糖果
- public void dispense() {
- // TODO Auto-generated method stub
- System.out.println("No gumball dispensed");
- }
- }
- //WinnerState.java
- package State;
- import State.GumballMachine;
- /*
- * 赢家状态,实现了State接口
- */
- public class WinnerState implements State{
- GumballMachine gumballMachine;
- public WinnerState(GumballMachine gumballMachine){
- this.gumballMachine = gumballMachine;
- }
- // 投入25分钱
- @Override
- public void insertQuarter() {
- // TODO Auto-generated method stub
- System.out.println("Please wait, we're already giving you a gumball");
- }
- // 拒绝25分钱
- @Override
- public void ejectQuarter() {
- // TODO Auto-generated method stub
- System.out.println("Sorry,you have already turn crank");
- }
- // 转动曲柄
- @Override
- public void turnCrank() {
- // TODO Auto-generated method stub
- System.out.println("trun twice ,doesn't give you anthor gamball!");
- }
- // 发放糖果
- @Override
- public void dispense() {
- // TODO Auto-generated method stub
- System.out.println("You're a Winner! You get two gumballs for your quarter");
- 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("Opps,out of gamball!");
- gumballMachine.setState(gumballMachine.getSoldOutState());
- }
- }
- }
- }
- //糖果机上下文环境类Java源文件 GumballMachine.java
- package State;
- import State.HasQuarterState;
- import State.NoQuarterState;
- import State.SoldOutState;
- import State.SoldState;
- import State.WinnerState;
- import State.State;
- /*
- * 糖果机器上下文环境接口类 GumballMachine
- */
- public class GumballMachine {
- //状态实例
- State soldOutState;
- State noQuarterState;
- State hasQuarterState;
- State soldState;
- State winnerState;
- // 实例变量state,初始化为糖果售罄状态
- State state = soldOutState;
- // 记录机器内装有糖果的数目,开始机器是没有装糖果的
- int count=0;
- // 构造器取得糖果的初始数目并把它放在一个实例变量count中
- 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;
- // 若超过0颗糖果,就将状态设置为NoQuarterState
- if(numberGumballs > 0) {
- state = noQuarterState;
- }
- }
- // 取得机器内的糖果数目
- public int getCount() {
- return count;
- }
- // 取得糖果售罄状态
- public State getSoldOutState() {
- return soldOutState;
- }
- // 取得没有25分钱状态
- public State getNoQuarterState() {
- return noQuarterState;
- }
- // 取得拥有25分钱
- public State getHasQuarterState() {
- return hasQuarterState;
- }
- // 取得售出糖果状态
- public State getSoldState() {
- return soldState;
- }
- // 取得赢家状态
- public State getWinnerState() {
- return winnerState;
- }
- // 投入25分钱
- public void insertQuarter(){
- state.insertQuarter();
- }
- // 拒绝25分钱
- public void ejectQuarter(){
- state.ejectQuarter();
- }
- // 转动曲柄
- public void turnCrank(){
- state.turnCrank();
- state.dispense();
- }
- // 设置状态
- public void setState(State state){
- this.state=state;
- }
- // 糖果滚出来一个
- public void releaseBall(){
- System.out.println("A gumball comes rolling out of the solt...");
- if(count!=0){
- count--;
- }
- }
- }
- //测试糖果机的Java源文件 GumballMachineTestDrive.java
- package State;
- /*
- * 糖果机测试驱动程序:GumballMachineTestDrive.java
- */
- public class GumballMachineTestDrive {
- /**
- * @param args
- */
- public static void main(String[] args) {
- GumballMachine gumballMachine = new GumballMachine(5);
- System.out.println(gumballMachine);
- System.out.println("The current gumball number is:" + gumballMachine.getCount());
- System.out.println("****************************************");
- gumballMachine.insertQuarter();
- gumballMachine.turnCrank();
- System.out.println(gumballMachine);
- System.out.println("The current gumball number is:" + gumballMachine.getCount());
- System.out.println("****************************************");
- gumballMachine.insertQuarter();
- gumballMachine.turnCrank();
- System.out.println(gumballMachine);
- System.out.println("The current gumball number is:" + gumballMachine.getCount());
- System.out.println("****************************************");
- gumballMachine.insertQuarter();
- gumballMachine.turnCrank();
- System.out.println(gumballMachine);
- System.out.println("The current gumball number is:" + gumballMachine.getCount());
- System.out.println("****************************************");
- }
- }
运行结果截图如下:
三、状态vs策略
而状态模式如定义中所言,我们将一群行为封装在状态对象中,context的行为随时可以委托到那些状态对象中的一个,随着时间的进行,当前状态在状态对象集合中游走改变,以反映出context内部的状态,因此context的行为也会改变;对于客户程序来言,这种状态变化往往是透明的。
ref:http://www.cnblogs.com/java-my-life/archive/2012/06/08/2538146.html
http://blog.csdn.net/ccf19881030/article/details/8257659
http://dev.yesky.com/438/2164938.shtml