一、含义
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。也就是说当一个对象有许多状态的时候,我们可以把每个对象抽离出来作为一个具体的类。
二、要点
1.状态模式允许一个对象基于内部状态而拥有不同的行为。
2.通过将每个状态封装进一个类,我们把以后需要做的任何改变局部化了。
3.使用状态模式通常会导致设计中类的数目大量增加。
三、实战分析外观模式
首先看一下状态模式的类图:
例如现在有需要自动售货机,现在假设一个自动售货机只卖一种饮料,只需5块钱,对于售货机有四种状态就是,没有投钱,投入5块钱,出售饮料,饮料售空。现在让我们用代码,来编写自动售货机这个收获流程:
public class DrinksMachine {
//对应自动售货机的四种状态
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 DrinksMachine(int count) {
this.count = count;
if (count > 0) {
state = NO_QUARTER;
}
}
//当我们投入5块钱时,会执行这个方法
public void insertQuarter() {
if (state == HAS_QUARTER) {
System.out.println("已投钱,请勿重复投币");
} else if (state == NO_QUARTER) {
state = HAS_QUARTER;
System.out.println("投币成功");
} else if (state == SOLD_OUT) {
System.out.println("请莫投币,饮料已售空");
} else if (state == SOLD) {
System.out.println("请等待,我们正在出货");
}
}
//当我们尝试退款时,执行的方法
public void ejectQuarter() {
if (state == HAS_QUARTER) {
System.out.println("正在退钱,请稍等");
state = NO_QUARTER;
} else if (state == NO_QUARTER) {
System.out.println("未投钱,无法退款");
} else if (state == SOLD) {
System.out.println("无法退钱,正在给你出货");
} else if (state == SOLD_OUT) {
System.out.println("饮料已售空,未首款");
}
}
//当我们按下按钮准备买饮料时,会调用此方法
public void turnCrank() {
if (state == SOLD) {
System.out.println("正在出货请稍等,不要重复按钮");
} else if (state == NO_QUARTER) {
System.out.println("还未投钱,请先投钱");
} else if (state == SOLD_OUT) {
System.out.println("不好意思,已售空");
} else if (state == HAS_QUARTER) {
System.out.println("出货");
state = SOLD;
sale();
}
}
//出售饮料方法
public void sale() {
if (state == SOLD) {
System.out.println("正在出货");
count --;
if (count == 0) {
System.out.println("已售空");
state = SOLD_OUT;
} else {
state = NO_QUARTER;
}
} else if (state == NO_QUARTER) {
System.out.println("请先投钱");
} else if (state == SOLD_OUT) {
System.out.println("饮料已售空");
} else if (state == HAS_QUARTER) {
System.out.println("机器故障");
}
}
}
从上述代码可以看出,我们每个方法都要对售货机的每个状态都要进行判断,这显得代码复用性不高,而且如果我们可以加入新的状态时,我们需要修改大量的代码,这违反了类应该对扩展开放,对修改关闭的原则。这个我们的状态模式就可以大显神威了,利用状态模式,重写这个自动收货机,结合上面的类图,我们需要把每个状态单独封装成一个类,并实现一个共同的接口:
//状态的通用接口
public interface state {
void insertQuarter();
void ejectQuarter();
void turnCrank();
void sale();
}
在编写对应的具体的状态类
//其中的一个例子,其他的三个状态大体相同
public class NoQuarterState implements State {
DrinksMachine drinksMachine;
public NoQuarterState(DrinksMachine drinksMachine) {
this.drinksMachine = drinksMachine;
}
//当有人投钱时
@Override
public void insertQuarter() {
System.out.println("你已投钱");
drinksMachine.setState(drinksMachine.getHasQuarterState());
}
@Override
public void ejectQuarter() {
System.out.println("你未投钱");
}
@Override
public void turnCrank() {
System.out.println("没有钱,无法出售");
}
@Override
public void sale() {
System.out.println("没有钱,无法出售");
}
}
最后我们在将自己售货机的代码给修改:
public class DrinksMachine {
//对应着自己状态
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State state = soldOutState;
int count = 0;
public DrinksMachine(int number) {
soldOutState = new SoldOutState(this);
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldState = new SoldState(this);
this.count = number;
if (number > 0) {
state = noQuarterState;
}
}
public void insertQuarter() {
state.insertQuarter();
}
public void ejectQuarter() {
state.ejectQuarter();
}
public void turnCrank() {
state.turnCrank();
}
public void sale() {
state.sale();
}
//每个状态的get/set方法
}
这看着上就简洁的多,并且在以后有新的状态时,只需添加新的状态类就可以。