状态模式适用于自己的程序或者某个对象有几种不同的状态,每个状态下都有不同的操作,这样的情况下我们可以采用状态模式。
为什么要采用状态模式
在我们介绍状态模式之前,我们先想一想我们会怎么写。最正常的想法无非是在每个方法里面先判断当前对象处于什么状态,也就是说,要采用大量的if else语句或者switch语句。而这样可维护性上面就比较差,试想,如果后期我们需要增添或者减少一个状态,是不是每一处判断对象状态的代码块都要做出修改?为了避免这样的麻烦,我们可以采用状态模式。
先看一个例子
这是一张描述状态的转换图。可以看到,有四种状态,并且他们之间可以完成相互转换。我们看一看状态模式下的代码是什么样的。
1.抽象状态类的实现
public abstract class State {
/**
* 投币
*/
public abstract void insertQuarter();
/**
* 退币
*/
public abstract void ejectQuarter();
/**
* 转动出糖曲轴
*/
public abstract void turnCrank();
/**
* 发糖
*/
public abstract void dispense();
/**
* 退还硬币
*/
protected void returnQuarter() {
System.out.println("退币……");
}
}
2.四个具体状态类的实现
/**
* 没有硬币的状态
*/
public class NoQuarterState extends State{
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("你投入了一个硬币");
//转换为有硬币状态
gumballMachine.setState(gumballMachine.hasQuarterState);
}
@Override
public void ejectQuarter() {
System.out.println("没有硬币,无法弹出");
}
@Override
public void turnCrank() {
System.out.println("请先投币");
}
@Override
public void dispense() {
System.out.println("没有投币,无法发放糖果");
}
}
/**
* 投硬币的状态
*/
public class HasQuarterState extends State{
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("请不要重复投币!");
returnQuarter();
}
@Override
public void ejectQuarter() {
returnQuarter();
gumballMachine.setState(gumballMachine.noQuarterState);
}
@Override
public void turnCrank() {
System.out.println("转动曲轴,准备发糖");
gumballMachine.setState(gumballMachine.soldState);
}
@Override
public void dispense() {
System.out.println("this method don't support");
}
}
/**
* 出售的状态
*/
public class SoldState extends State{
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("已投币,请等待糖果");
returnQuarter();
}
@Override
public void ejectQuarter() {
System.out.println("无法退币,正在发放糖果,请等待");
}
@Override
public void turnCrank() {
System.out.println("已按过曲轴,请等待");
}
@Override
public void dispense() {
int candyCount = gumballMachine.getCandyCount();
if(candyCount > 0){
System.out.println("分发一颗糖果");
candyCount--;
gumballMachine.setCandyCount(candyCount);
if(candyCount > 0){
gumballMachine.setState(gumballMachine.noQuarterState);
return;
}
}
System.out.println("抱歉,糖果已售尽");
gumballMachine.setState(gumballMachine.soldOutState);
}
}
/**
* 售尽的状态
*/
public class SoldOutState extends State{
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("糖果已经售尽");
returnQuarter();
}
@Override
public void ejectQuarter() {
System.out.println("没有投币,无法退币");
}
@Override
public void turnCrank() {
System.out.println("糖果已经售尽");
}
@Override
public void dispense() {
System.out.println("糖果已经售尽");
}
}
我们来分析一下这四个具体状态类的实现。首先,每一个具体类中,都有一个糖果机的引用。之所以有这个成员,是因为我们要通过这个成员来对我们的糖果机的状态进行更改。在看构造方法:传参为糖果机类型。之后我们看每个方法的具体实现。在不同的状态下,对应了不同的提示,以及状态的转换。
3.糖果机的实现
public class GumballMachine{
public State noQuarterState = new NoQuarterState(this);
public State hasQuarterState = new HasQuarterState(this);
public State soldState = new SoldState(this);
public State soldOutState = new SoldOutState(this);
private State state = soldOutState;
private int candyCount = 0;
public GumballMachine(int count) {
this.candyCount = count;
if(count > 0)
setState(noQuarterState);
}
public void insertQuarter() {
state.insertQuarter();
}
public void ejectQuarter() {
state.ejectQuarter();
}
public void turnCrank() {
state.turnCrank();
}
public void dispense() {
state.dispense();
}
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public void setCandyCount(int candyCount) {
this.candyCount = candyCount;
}
public int getCandyCount() {
return candyCount;
}
}
首先,可以清楚地看到,这里有许多表示状态的成员对象,这些就是用来表示状态以及做出特定状态下的动作的。此外还有一个public的State,这个状态是用来展示当前机器处于那种状态,前面的私有状态是不变的,而这个公有的state是在各个状态之前来回变化的。
至此,如果能定义好初始状态,那么我们调用糖果机里面的几种动作方法,就会调用state所指对象的方法,这样就能在糖果机不可见的地方实现了状态的切换。
这也就是状态模式的基本形式了。