场景引出

假设你有这样一种需求:零售系统中的订单通常有多种状态,包括了从支付、出库、发货、签收等各种状态。而系统则需要根据这些不同的订单状态分别做对应的处理。一个比较符合直觉的解决方案可能就是用多重条件分支语句。就像下面这样:

1
2
3
4
5
6
7
8
9
if(order.state === 'payment') {
//处理支付订单
} else if(order.state === 'outbound') {
//处理出库
}else if(order.state === 'delivery') {
//处理发货
}else if(order.state === 'received') {
//处理签收
}

但是,为了在后续代码有更好的可维护性,有没有更好的解决方案呢?这里就引出了状态模式。

状态模式的定义

状态模式(State Pattern): 允许一个对象在其内部状态改变时改变它的行为。在设计模式中,它是处于对象行为型模式。

  • 上下文(context): 环境,即具体执行的上下文。是包含了状态的对象,可以充当状态管理器(state manager)对状态进行切换操作。
  • 抽象状态(State): 抽象状态用以定义各种状态间公用的方法和接口,以便继承。
  • 具体状态(ConcreteState): 将对象的各种状态抽象出来,具体的业务逻辑也是在具体状态类中实现。同时,各个状态之间的转换关系也在具体状态类中得以体现。

解决方案

至此,我们就可以将上述的场景代码改成如下所示:

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
var State = function() {};
State.prototype.handle = function() {};

var Order = function() {
this.state = new PaymentState(); //假设初始化状态为支付状态。
this.setState = function(state) {
this.state = state;
}
};

//处理订单
Order.prototype.handle = function() {
this.state.handle(this);
}

//支付状态
var PaymentState = function() {
this.handle = function(order) {
//利用传进来的order处理
...
//订单处理完毕后将转换状态
order.setState(new OutboundState());
}
};

//出库状态
var OutboundState = function() {
this.handle = function(order) {
...
order.setState(new )
}
}

var order = new Order();
order.handle();

优缺点分析

状态模式和策略模式有点类似,都是讲具体的业务行为委托给具体的子类,从而避免多重条件分支语句造成的代码维护和耦合问题。

但同时使用状态模式,不可避免地将会增加状态类和系统类的个数,当状态数量较多以及转换规则复杂时,容易造成代码混乱。如果需要增加新的状态时候,也需要修改具体状态转换的代码,以及相关的具体状态类的代码。