状态模式:
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
状态模式的结构:
- 环境(Context)角色,也称上下文:定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。
- 抽象状态(State)角色:定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。
- 具体状态(ConcreteState)角色:每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。
源代码:
环境角色类:
public class Context {//持有一个State类型的对象实例private State state;public void setState(State state) { this.state = state; }//用户感兴趣的接口方法public void request(String s) {//转调state来处理state.handle(s);}
}
抽象状态角色:
public interface State {//状态对应的处理public void handle(String s);
}
具体状态角色:
//具体状态A
public class ConStateA implements State {public void handle(String s) {System.out.println("ConcreteStateA :" + s);}}
//具体状态B
public class ConStateB implements State {public void handle(String s) {System.out.println("ConcreteStateB :" + s);}}
测试方法:
public class Client {public static void main(String[] args){//创建状态State state = new ConcreteStateB();//创建环境Context context = new Context();//将状态设置到环境中context.setState(state);//请求context.request("test");}
}
示例:糖果机
糖果机工作如上图所示,这个例子中,糖果机是环境,每一个圆圈都是一个具体状态,而每一个箭头都是状态之间的转换。
我们使用状态模式来重写代码:
- 首先定义一个state接口。这个接口内糖果机每个动作都有一个对应的方法。
- 然后为机器中的每个状态实现状态类。这些类将负责在对应的状态下进行机器的行为。
- 最后将动作委托到状态类。
实现state接口,每个状态类都要实现该接口:
public interface State {public void insertQuarter();public void ejectQuarter();public void turnCrank();public void dispense();
}
实现糖果机类:
public class GumballMachine {//所有的状态都在这里State soldOutState;State noQuarterState;State hasQuarterState;State soldState;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);this.count = numberGumballs;if (numberGumballs > 0) {state = noQuarterState;} }public void insertQuarter() {//委托当前状态state.insertQuarter();}public void ejectQuarter() {//委托当前状态state.ejectQuarter();}public void turnCrank() {//注意这里和其他两个的区别。dispense是一个内部动作,用户不能直接要求发放糖果。用户转动手柄turnCrack()方法调用dispense()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;state = noQuarterState;}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;}//更多方法
}
实现状态类:
import java.util.Random;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 String toString() {return "waiting for turn of crank";}
}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 String toString() {return "waiting for quarter";}
}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 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 String toString() {return "dispensing a gumball";}
}
要点:
- 状态模式允许一个对象基于内部状态而拥有不同的行为。
- 和程序状态机(PSM)不同,状态模式用类代表状态。
- Context会将行为委托给当前状态对象。
- 通过将每个状态封装进一个类,我们把以后需要做的任何改变局部化了。
- 状态模式和策略模式有相同的类图,但他们的意图不同。
- 使用状态模式通常会导致设计中类的数目大量增加。
- 状态类可以被多个Context实例共享。