理解设计模式之状态模式、职责链模式

状态模式是状态传递的抽象,职责链是事件传递的抽象,有一些异曲同工之妙。
下面就来分析一下这两个模式。

状态模式

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

从这个意图中,有两个地方需要明确::
第一个问题,内部状态的改变如何改变行为?这个好理解,让你的行为(你可以通俗的理解为方法)依赖于这个内部状态,当内部状态改变时,行为就会发生变化。
比如下面这段代码,同样调用change()操作的行为结果确有开有关,是根据state变化的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 开关
public class Switch {
public static final int ON = 0;
public static final int OFF = 1;
public int state = ON;
public void change() {
if (state == ON) {
state = OFF;
} else if (state == OFF) {
state = ON;
}
}
}

第二个问题,对象看起来似乎修改了它的类?这句话说起来有点绕口,说白了,就是对象发生了变化,且是变成了不同的类的对象。一个对象的类可以变来变去必定是实现了同一接口。凭着感觉,应该能抽象出这个State类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface IState {
// 行为的方法名,可以随便取
void handle();
}
public class StateA implements IState {
@Override
public void handle() {
}
}
public class StateB implements IState {
@Override
public void handle() {
}
}

上面说的有点冗长,还是回归到状态模式本身。
Swich类的change方法的处理,是一种典型的面向过程式的代码,有两个方面考虑:
第一,if-else让状态的扩展变的很难。
第二,状态的变换其实是有关联的,ON-OFF-ON(如果是A,B,C的话,就是A-B-C),不是简单的独立分支。这种关联关系包含在State本身会是更好的结构,分离Switch对状态的逻辑处理。
我们把它面向对象化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface IState {
void handle(Context context);
}
public class StateA implements IState {
@Override
public void handle(Context context) {
context.setState(new StateB());
}
}
public class StateB implements IState {
@Override
public void handle(Context context) {
context.setState(new StateC());
}
}
public class StateC implements IState {
@Override
public void handle(Context context) {
// if it has StateD, change to StateD.
// or game over.
}
}

Context指上下文(比如Switch),需要传给State,让具体State能更新Context的State对象(正如意图中说的,对象看起来似乎修改了它的类):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Context {
public IState state;
public IState getState() {
return state;
}
public void setState(IState state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}

现在好了,没有if-else的“丑陋”,可以增加或者删除状态对象,对Context无影响。同时Context也没有了状态逻辑,非常清爽。
所以,状态模式常常用于具备状态分支if-else有不同的行为且状态是关联的,UML类图如下:
状态模式UML类图

职责链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这
些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

在理解状态模式的基础上,再理解职责链模式就简单了。
状态模式强调的是状态的自传递,如果改为行为的自传递,就是职责链模式了。
相对于状态模式的被动方式(每次状态变化都需要Context的request一下),职责链模式则是主动式的把行为自行处理一直传递下去直到处理完成,而且传递的顺序依赖于client的组装。

把状态改为行为,稍微实现一下:

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
public abstract class Handler {
// 后继者
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handleRequest();
}
public class HandlerA extends Handler {
@Override
public void handleRequest() {
System.out.println("A is handler ...");
successor.handleRequest();
}
}
public class HandlerB extends Handler {
@Override
public void handleRequest() {
// 如果处理好了就不用传递给后面
// if (someflag) { return; }
System.out.println("B is handler ...");
successor.handleRequest();
}
}
public class HandlerC extends Handler {
@Override
public void handleRequest() {
System.out.println("C is handler ... END!");
}
}

client自行组装链条(建立一个指导者Director也是可以的):

1
2
3
4
5
6
7
8
9
Handler handlerA = new HandlerA();
Handler handlerB = new HandlerB();
Handler handlerC = new HandlerC();
// 组装链条
handlerA.setSuccessor(handlerB);
handlerB.setSuccessor(handlerC);
handlerA.handleRequest();

A处理不了就交给B,B处理不了就交给C,C处理不了那就没办法了。
这里没有if-else,具体行为是各个Handler自行负责,把行为巧妙的传递下去,常用于事件传递,UML类图如下:
职责链模式UML类图

小结

行为型模式很多都有类似的地方,状态模式和职责链模式分别对状态和行为的传递做了抽象,这也是过程式转化面向对象的一个实践,值得参考。
过程式里面还有哪些东西可以用面向对象化的方式表达呢?请思考。