Earth Guardian

You are not LATE!You are not EARLY!

0%

设计模式--行为型:状态模式

状态模式:当对象的内部状态改变时允许改变其行为,该对象看起来像是改变了其类

状态模式

状态模式 State Pattern:主要是解决当控制一个对象状态转移的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。

类图结构

0068-State-uml-classdiag.png

结构解析

  • State
    抽象类,状态接口,定义当前状态的行为。
  • ConcreteState
    实现类,实现当前状态的行为,并切换状态。
  • Context
    环境类,上下文,持有当前状态的引用,维护状态机的状态切换,响应客户端的请求,并转换为状态的具体行为。

示例

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 1. State
public abstract class State {
public abstract void handle(Context context);
}

// 2. ConcreteState
public class ConcreteStateA extends State{
@Override
public void handle(Context context) {
System.out.println("ConcreteStateA...");
context.setState(new ConcreteStateB());
}
}

public class ConcreteStateB extends State{
@Override
public void handle(Context context) {
System.out.println("ConcreteStateB...");
context.setState(new ConcreteStateC());
}
}

public class ConcreteStateC extends State{
@Override
public void handle(Context context) {
System.out.println("ConcreteStateC");
}
}

// 3. Context
public class Context {
private State state;

public Context() {
state = new ConcreteStateA();
}

public void setState(State state){
this.state = state;
}

public void request(){
System.out.print("Context.request: ");
state.handle(this);
}
}

// 4. Test
public class TestState {
public static void main(String[] args) {
Context context = new Context();
context.request();
context.request();
context.request();
}
}

// 5. Result
Context.request: ConcreteStateA...
Context.request: ConcreteStateB...
Context.request: ConcreteStateC

总结

状态模式是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,可以使用状态模式。状态模式通过把各种状态转移逻辑分别到各个 State 子类之间,来减少相互间的依赖。

状态模式与责任链模式的异同

  • 相同点
    都能解耦和优化大量的逻辑判断。
  • 不同点
    状态模式每个子类能清楚的直到下个状态是谁或者哪个子类,相当于 if-else 分支判断;而责任链模式并不清楚整个链条中是哪个子类处理的,下个责任人也需要客户端来指定,更灵活,相当于 switch-case 分支判断。
    通俗来讲,状态模式的下一个状态是明确的,固定的;而责任链中下一个处理者并不固定,需要客户端动态指定。

参考文档