设计模式学习笔记(16)状态

本文实例代码:https://github.com/JamesZBL/java_design_patterns

状态模式(State)最明显的特点是可以使类的行为根据不同的状态变化而变化,很明显,这是一种行为型模式。通常用一个上下文对象将某个类的对象进行一层包裹,将该类在不同状态下的具体行为分别交给不同状态的上下文类来实现。该模式的完美的将类的行为和它的状态进行了解耦,如果某个类增添一种状态,只需要增加新的状态类就可以了。

实例

我们在开发过程中难免会遇到一些 bug,有些非常棘手的 bug 会让我们非常焦虑,一旦解决了这些问题,我们就如释重负的放松下来了。我们在心情平静时和被 bug 困扰时就是分别处于两种不同的状态,而我们在这两种状态下的行为表现也有所不同。

定义一个 State 接口,它包含了所有状态类的行为:

State.java

1
2
3
4
5
6
7
public interface State {

// 进入状态之前
void onPreparing();
// 进入状态之后
void onEnterState();
}

定义一个 Code 类, 它有两种行为表现 —— 进入状态前的表现和进入状态后的表现:

Coder.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Coder {

private State state;

public Coder() {
this.state = new IdleState(this);
}

/**
* 切换状态
*/
private void changeStateTo(State newState) {
state.onEnterState();
this.state = newState;
}

/**
* 模拟过了一段时间
*/
public void timePass() {
if (state.getClass().equals(ImpatientState.class)) {
changeStateTo(new IdleState(this));
} else {
changeStateTo(new ImpatientState(this));
}
}

/**
* 准备进入状态
*/
public void prepare() {
state.onPreparing();
}
}

两种不同的状态类,它们都实现 State 接口:

IdleState.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* 平静的状态
*/
public class IdleState implements State {

private static final Logger LOGGER = LoggerFactory.getLogger(IdleState.class);

private Coder coder;

public IdleState(Coder coder) {
this.coder = coder;
}

@Override
public void onPreparing() {
LOGGER.info("{}正努力使自己变得平静", coder);
}

@Override
public void onEnterState() {
LOGGER.info("{}正悠闲的听着歌", coder);
}
}

ImpatientState.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* 焦虑的状态
*/
public class ImpatientState implements State {

private static final Logger LOGGER = LoggerFactory.getLogger(ImpatientState.class);

private Coder coder;

public ImpatientState(Coder coder) {
this.coder = coder;
}

@Override
public void onPreparing() {
LOGGER.info("{}面对一堆 bug,开始逐渐焦躁起来", coder);
}

@Override
public void onEnterState() {
LOGGER.info("{}已经被 bug 搞的进入了极度狂躁的状态", coder);
}
}

来模拟一下程序员的这两种状态:

App.java

1
2
3
4
5
6
7
8
9
10
11
public class Application {

public static void main(String[] args) {
Coder coder = new Coder();
coder.prepare();
coder.timePass();
coder.prepare();
coder.timePass();
coder.prepare();
}
}

总结

状态模式解决了当一个类有多种不同状态时设计过于复杂的问题,它将类在不同状态下的行为和该类本身进行了解耦,将这些行为独立到另一个接口中,该类的状态发生改变后只需要添加新的类即可。

之前文章中的命令模式的接口中只有一个方法,但状态模式的接口中有可以有更多方法,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。

状态模式封装了转换规则,所以用枚举的形式判断目前的状态,根据不同的状态确定使用的具体状态实现类。将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。允许状态转换逻辑与状态对象合成一体,这就避免了一个很大的条件判断代码块。

状态模式的使用会增加系统中类和对象的个数,所以状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。状态模式虽然可以方便的添加新的状态实现类,但它也并不是完全支持”开闭原则”的,因为当状态需要切换的时候,增加新的状态类同样还要要修改和状态转换相关的代码,而且修改某个状态实现类的行为都要修改那个类。

分享到: