用类表示状态
1.基本介绍
状态模式使用对象的形式来记录某种状态。使用状态模式可以省去多个if-else或者是switch的判断。可以直接使用对象方法的形式来处理逻辑。
我们在编写代码的时候,遇到大量的条件判断的时候,可能会采用策略模式来优化结构,因为这时涉及到策略的选择,但有时候仔细查看下,就会发现,这些所谓的策略其实是对象的不同状态,更加明显的是,对象的某种状态也成为判断的条件。
2.实例分析
假设现在我们有一个饮水机,它有以下两个状态: 满桶,空桶。初始状态是满桶,容量是20。饮水机只有一个动作:press,每次press后都会使容量减1,一旦为0,则将状态设置为空桶,这时press没有水流出。
要使用状态模式,我们必须明确两个东西:状态和每个状态下执行的动作。就像是饮水机,最基本的状态就是满桶和空桶,而这两个状态下,都可能要执行倒水这个动作,也就是press。如果饮水机的容量为0,则会进入空桶的状态。
在状态模式中,因为所有的状态都要执行相应的动作,所以我们可以考虑将状态抽象出来。
状态的抽象一般有两种形式:接口和抽象类。如果所有的状态都有共同的数据域,可以使用抽象类,但如果只是单纯的执行动作,就可以使用接口。
接口:
public interface DispenserState {
void press();
}
定义满桶和空桶两个状态
/**
* @author Jay
* @date 2019/6/5 22:40
* @description 空桶
*/
public class NullState implements DispenserState {
@Override
public void press() {
System.out.println("There is not water poured!");
}
}
/**
* @author Jay
* @date 2019/6/5 22:39
* @description 满桶
*/
public class FullState implements DispenserState {
@Override
public void press() {
System.out.println("Water is pouring!");
}
}
实现饮水机
/**
* @author Jay
* @date 2019/6/5 22:41
* @description 实现饮水机
*/
public class WaterDispenser {
private static int capacity = 20;
private static DispenserState dispenserState;
public WaterDispenser(DispenserState state) {
dispenserState = state;
}
private static void setState(DispenserState state) {
dispenserState = state;
}
public DispenserState getState() {
return dispenserState;
}
public void press() {
capacity--;
if (capacity <= 0) {
// 只需要改变状态即可
setState(new NullState());
}
dispenserState.press();
}
}
测试:
/**
* @author Jay
* @date 2019/6/5 22:49
* @description
*/
public class Test {
public static void main(String[] args) {
WaterDispenser dispenser = new WaterDispenser(new FullState());
for (int i = 0; i < 100; ++i) {
dispenser.press();
}
}
}
状态模式的好处就是将我们从这个复杂的嵌套条件中脱离出来,但状态模式的坏处也是非常明显:需要管理一系列的状态类。
状态模式的意图就是让每一个状态对修改关闭,允许对象在内部状态改变时改变它的行为,使对象看起来好像修改了它的类。
让每一个状态对修改关闭,也就是让状态类来改变状态,即封装,我们只能通过向状态类发送消息来改变状态,至于怎么改变,则完全隐藏起来。
允许对象在内部状态改变时改变它的行为,使对象看起来好像修改了它的类。其实,对象本身拥有一个状态类的对象集合,只要符合状态改变的条件,就可以将表示状态的状态类改变成下个状态类。从具体的代码来看,所谓的状态类的对象集合,其实就是我们的对象拥有一个状态类的引用,该引用可以指向任何状态类的对象集合的具体引用。对象本身一般都会有一个setState()方法,可以将状态修改成另一个状态。
状态模式的意图其实非常简单:将与状态有关的处理逻辑分散到代表对象状态的各个类中,但我们的Context类必须拥有这些状态对象集合的引用,这也就引出一个问题:实例化Context类对象会导致状态类对象集合初始化方面的问题,也就是对象的依赖问题。
为了尽可能减少这方面的依赖,我们的Context类通常拥有的只是一个状态类的抽象引用,然后设置一个setter以便动态的更改状态类对象
如果不是使用对象,而是采用常量的方法。
private static final int FULL_STATE = 0;
private static final int NULL_STATE = 1;
enum STATE{ NULL_STATE, FULL_STATE};
4.模式总结
State(状态)
State角色表示状态,定义了根据不同状态进行不同处理的接口(API)。该接口(API)是那些处理内容依赖于状态的方法的集合。
ConcreteState(具体状态)
ConcreteState角色表示各个具体的状态,它实现了State接口。
Context(状况、前后关系、上下文)
Context角色持有表示当前状态的ConcreteState角色。此外,它还定义了供外部调用者使用State模式的接口(API)。