- 定义:
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。 -
UML:
- 模型:
投币的糖果机,糖果机根据内部的状态,表现出是否可以投币,是否可以退币,是否要掉落糖果等,例如,在未投币的状态下是不能退币的,在已投币未掉落糖果的情况下也不能再次投币的。而状态模式就能很好的解决这样复杂的逻辑判断,解决的方式是将这些逻辑转移到多个类中。
//首先我们抽象出所有的状态下都应该有的行为。
public interface State {
//投硬币
public void insertCoin();
//返回硬币
public void returnCoin();
//转动把手 出糖果
public void turnCrank();
//掉落糖果
public void dispense();
//输出状态
public void printstate();
}
//构建一个糖果机
public class CandyMachine {
State mSoldOutState;
State mOnReadyState;
State mHasCoin;
State mSoldState;
State mWinnerState;
private State state;
private int count = 0;
//组合多种状态
public CandyMachine(int count) {
this.count = count;
mSoldOutState = new SoldOutState(this);
mOnReadyState = new OnReadyState(this);
mHasCoin = new HasCoin(this);
mSoldState = new SoldState(this);
mWinnerState = new WinnerState(this);
if (count > 0) {
state = mOnReadyState;
} else {
state = mSoldOutState;
}
}
//糖果机的行为完全受当前的状态决定
public void setState(State state) {
this.state = state;
}
public void insertCoin() {
state.insertCoin();
}
public void returnCoin() {
state.returnCoin();
}
public void turnCrank() {
state.turnCrank();
state.dispense();
}
void releaseCandy() {
if (count > 0) {
count = count - 1;
System.out.println("a candy rolling out!");
}
}
public int getCount() {
return count;
}
public void printstate() {
state.printstate();
}
}
//糖果机可用状态
public class OnReadyState implements State {
private CandyMachine mCandyMachine;
public OnReadyState(CandyMachine mCandyMachine){
this.mCandyMachine=mCandyMachine;
}
//当前状态只能投入硬币,不提供其他行为
@Override
public void insertCoin() {
System.out
.println("you have inserted a coin,next,please turn crank!");
//当投入硬币后,更改糖果机的状态
mCandyMachine.setState(mCandyMachine.mHasCoin);
}
@Override
public void returnCoin() {
System.out.println("you haven't inserted a coin yet!");
}
@Override
public void turnCrank() {
System.out.println("you turned,but you haven't inserted a coin!");
}
@Override
public void dispense() {}
@Override
public void printstate() {
System.out.println("***OnReadyState***");
}
}
//有硬币的状态
public class HasCoin implements State {
private CandyMachine mCandyMachine;
public HasCoin(CandyMachine mCandyMachine) {
this.mCandyMachine = mCandyMachine;
}
@Override
public void insertCoin() {
System.out.println("you can't insert another coin!");
}
//当前状态可用回退硬币
@Override
public void returnCoin() {
System.out.println("coin return!");
mCandyMachine.setState(mCandyMachine.mOnReadyState);
}
//当前状态可用转动把手掉落糖果
@Override
public void turnCrank() {
System.out.println("crank turn...!");
Random ranwinner=new Random();
int winner=ranwinner.nextInt(10);
//设置一个幸运糖果,还有一次获取糖果的机会
if(winner==0){
mCandyMachine.setState(mCandyMachine.mWinnerState);
}else{
mCandyMachine.setState(mCandyMachine.mSoldState);
}
}
@Override
public void dispense() {}
@Override
public void printstate() {
System.out.println("***HasCoin***");
}
}
//销售状态
public class SoldState implements State {
private CandyMachine mCandyMachine;
public SoldState(CandyMachine mCandyMachine){
this.mCandyMachine=mCandyMachine;
}
@Override
public void insertCoin() {
System.out.println("please wait!we are giving you a candy!");
}
@Override
public void returnCoin() {
System.out.println("you haven't inserted a coin yet!");
}
@Override
public void turnCrank() {
System.out
.println("we are giving you a candy,turning another get nothing,!");
}
@Override
public void dispense() {
//掉落糖果
mCandyMachine.releaseCandy();
//根据当前糖果的数量设置下一个状态
if (mCandyMachine.getCount() > 0) {
mCandyMachine.setState(mCandyMachine.mOnReadyState);
} else {
System.out.println("Oo,out of candies");
mCandyMachine.setState(mCandyMachine.mSoldOutState);
}
}
@Override
public void printstate() {
System.out.println("***SoldState***");
}
}
//得到幸运糖果的状态,可以再次获得一次糖果
public class WinnerState implements State {
private CandyMachine mCandyMachine;
public WinnerState(CandyMachine mCandyMachine) {
this.mCandyMachine = mCandyMachine;
}
@Override
public void insertCoin() {
System.out.println("please wait!we are giving you a candy!");
}
@Override
public void returnCoin() {
System.out.println("you haven't inserted a coin yet!");
}
@Override
public void turnCrank() {
System.out
.println("we are giving you a candy,turning another get nothing,!");
}
@Override
public void dispense() {
mCandyMachine.releaseCandy();
if (mCandyMachine.getCount() == 0) {
mCandyMachine.setState(mCandyMachine.mSoldOutState);
} else {
System.out.println("you are a winner!you get another candy!");
//再次获得糖果。但是不能再是幸运糖果了
mCandyMachine.releaseCandy();
if (mCandyMachine.getCount() > 0) {
mCandyMachine.setState(mCandyMachine.mOnReadyState);
} else {
System.out.println("Oo,out of candies");
mCandyMachine.setState(mCandyMachine.mSoldOutState);
}
}
}
@Override
public void printstate() {
System.out.println("***WinnerState***");
}
}
//卖光了状态
public class SoldOutState implements State {
private CandyMachine mCandyMachine;
public SoldOutState(CandyMachine mCandyMachine){
this.mCandyMachine=mCandyMachine;
}
@Override
public void insertCoin() {
System.out.println("you can't insert coin,the machine sold out!");
}
@Override
public void returnCoin() {
System.out
.println("you can't return,you haven't inserted a coin yet!");
}
@Override
public void turnCrank() {
System.out.println("you turned,but there are no candies!");
}
@Override
public void dispense() {}
@Override
public void printstate() {
System.out.println("***SoldOutState***");
}
}
测试
public class MainTest {
public static void main(String[] args) {
CandyMachine mCandyMachine = new CandyMachine(6);
mCandyMachine.printstate();
mCandyMachine.insertCoin();
mCandyMachine.printstate();
mCandyMachine.turnCrank();
mCandyMachine.printstate();
mCandyMachine.insertCoin();
mCandyMachine.printstate();
mCandyMachine.turnCrank();
mCandyMachine.printstate();
}
}
- 总结:
如果是按照传统的面向对象的方式,糖果机自己维护当前的状态,那么糖果机对外提供的每个方法都会去switch每一个状态,做不同的判断处理,还要考虑当前的糖果个数,想想逻辑就很复杂,很容易出错。而状态模式,将这些复杂的判断都转移到每个状态类中,状态类最清楚那些方法该怎么处理。糖果机只需要关心自己有几种状态,提供那些方法使用。除此之外,状态模式很像策略模式,其实他们的UML是一样的,但意图却完全不一样,策略模式是让用户指定更换的策略算法,而状态模式是状态在满足一定条件下的自动更换,用户无法指定状态,最多只能设置初始状态。
总的来说:态模式关键是各个状态子类必须知道下一个状态是啥,且要把逻辑判断转移到各个状态子类中,客户端不需要了解状态迁移的顺序,但是策略目的是针对单一算法的运行时替换,客户端需要事先了解策略,主动去选择合适的策略,不存在状态的自动迁移!